@kendawson-online/vantl 2.0.6 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"timeline.min.js","sources":["../src/js/shared/state.js","../src/js/features/modals.js","../src/js/features/deep-linking.js","../src/js/shared/config.js","../src/js/features/loader-ui.js","../src/js/features/error-ui.js","../src/js/shared/utils.js","../src/js/features/colors.js","../src/js/core/timeline-engine.js","../src/js/features/data-loader.js","../src/js/timeline.js"],"sourcesContent":["// Shared state containers\nexport const loaderState = {\n count: 0,\n startTime: 0,\n removeTimer: null,\n overlayEl: null\n};\n\nexport const modalState = {\n modal: null,\n overlay: null\n};\n\n// Global registry to store timeline instances for programmatic navigation\nexport const timelineRegistry = {};\n","import { modalState } from '../shared/state.js';\n\nexport function createTimelineModal() {\n if (modalState.modal) return;\n\n modalState.overlay = document.createElement('div');\n modalState.overlay.className = 'timeline-modal-overlay';\n modalState.overlay.addEventListener('click', closeTimelineModal);\n\n modalState.modal = document.createElement('div');\n modalState.modal.className = 'timeline-modal';\n modalState.modal.innerHTML = `\n <button class=\"timeline-modal__close\" aria-label=\"Close modal\"></button>\n <div class=\"timeline-modal__content\">\n <img class=\"timeline-modal__image\" src=\"\" alt=\"\" style=\"display: none;\">\n <h2 class=\"timeline-modal__title\"></h2>\n <div class=\"timeline-modal__text\"></div>\n <hr class=\"timeline-modal__divider\">\n <button class=\"timeline-modal__close-bottom\">Close</button>\n </div>\n `;\n\n const closeBtn = modalState.modal.querySelector('.timeline-modal__close');\n const closeBottomBtn = modalState.modal.querySelector('.timeline-modal__close-bottom');\n closeBtn.addEventListener('click', closeTimelineModal);\n closeBottomBtn.addEventListener('click', closeTimelineModal);\n\n modalState.modal.addEventListener('click', function(e) {\n e.stopPropagation();\n });\n\n document.body.appendChild(modalState.overlay);\n document.body.appendChild(modalState.modal);\n\n document.addEventListener('keydown', function(e) {\n if (e.key === 'Escape' && modalState.modal.classList.contains('timeline-modal-show')) {\n closeTimelineModal();\n }\n });\n}\n\nexport function openTimelineModal(itemEl) {\n if (!modalState.modal) {\n createTimelineModal();\n }\n\n const title = itemEl.getAttribute('data-modal-title');\n const content = itemEl.getAttribute('data-modal-content');\n const image = itemEl.getAttribute('data-modal-image');\n const html = itemEl.getAttribute('data-modal-html');\n\n const modalTitle = modalState.modal.querySelector('.timeline-modal__title');\n const modalText = modalState.modal.querySelector('.timeline-modal__text');\n const modalImage = modalState.modal.querySelector('.timeline-modal__image');\n\n modalTitle.textContent = title || '';\n\n if (image) {\n modalImage.src = image;\n modalImage.alt = title || '';\n modalImage.style.display = 'block';\n } else {\n modalImage.style.display = 'none';\n }\n\n if (html) {\n modalText.innerHTML = html;\n } else if (content) {\n modalText.innerHTML = '<p>' + content.replace(/\\n/g, '</p><p>') + '</p>';\n } else {\n modalText.innerHTML = '';\n }\n\n setTimeout(function() {\n modalState.modal.classList.add('timeline-modal-show');\n modalState.overlay.classList.add('timeline-modal-show');\n document.body.style.overflow = 'hidden';\n }, 10);\n}\n\nexport function closeTimelineModal() {\n if (modalState.modal) {\n modalState.modal.classList.remove('timeline-modal-show');\n modalState.overlay.classList.remove('timeline-modal-show');\n document.body.style.overflow = '';\n }\n}\n","import { timelineRegistry } from '../shared/state.js';\n\nexport function handleDeepLinking(containerSelector) {\n const urlParams = new URLSearchParams(window.location.search);\n const timelineId = urlParams.get('timeline');\n const nodeId = urlParams.get('id');\n if (!nodeId) return;\n\n let targetContainer;\n if (timelineId) {\n targetContainer = document.getElementById(timelineId);\n } else {\n targetContainer = document.querySelector(containerSelector);\n }\n\n if (!targetContainer) {\n console.warn('Timeline not found for deep linking:', timelineId || containerSelector);\n return;\n }\n\n targetContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });\n\n const targetNode = targetContainer.querySelector('[data-node-id=\"' + nodeId + '\"]');\n if (targetNode) {\n setTimeout(function() {\n targetNode.classList.add('timeline__item--active');\n const itemIndex = Array.from(targetNode.parentNode.children).indexOf(targetNode);\n navigateToNodeIndex(targetContainer, itemIndex);\n }, 500);\n }\n}\n\nexport function navigateToNodeIndex(container, index) {\n if (!container) return;\n const timelineId = container.id || container.getAttribute('data-timeline-id');\n if (!timelineId) {\n console.warn('Cannot navigate: timeline container has no ID');\n return;\n }\n\n const tlData = timelineRegistry[timelineId];\n if (!tlData) {\n console.warn('Timeline not found in registry:', timelineId);\n return;\n }\n\n if (!container.classList.contains('timeline--horizontal')) {\n return;\n }\n\n if (tlData.setCurrentIndex && tlData.updatePosition) {\n tlData.setCurrentIndex(index);\n tlData.updatePosition();\n }\n}\n","// Shared configuration values\n// Auto-detect the timeline.js script location to build correct image paths\nexport const timelineBasePath = (function() {\n // Check for user override\n if (typeof window !== 'undefined' && \n window.TimelineConfig && \n window.TimelineConfig.basePath) {\n return window.TimelineConfig.basePath;\n }\n \n const scripts = document.getElementsByTagName('script');\n for (let i = 0; i < scripts.length; i++) {\n const src = scripts[i].src || '';\n if (!src) continue;\n const dir = src.substring(0, src.lastIndexOf('/'));\n if (src.indexOf('timeline.min.js') !== -1) {\n // When loading from dist, map to src/images\n return dir.replace('/dist', '/src/images');\n }\n if (src.indexOf('timeline.js') !== -1) {\n // When loading from src/js, map to src/images\n return dir.replace('/js', '/images');\n }\n }\n // Fallback relative to demo pages; most demos live under demo/**\n return '../src/images';\n})();\n\n// Minimum time (ms) to keep the loading spinner visible\nlet timelineLoaderMinMs = 1300;\nexport function getTimelineLoaderMinMs() {\n return timelineLoaderMinMs;\n}\nexport function setTimelineLoaderMinMs(value) {\n if (typeof value === 'number' && value >= 0) {\n timelineLoaderMinMs = value;\n }\n}\n","import { loaderState } from '../shared/state.js';\nimport { getTimelineLoaderMinMs, timelineBasePath } from '../shared/config.js';\n\nexport function showTimelineLoader() {\n loaderState.count += 1;\n if (loaderState.count !== 1) return;\n\n loaderState.startTime = Date.now();\n\n if (loaderState.removeTimer) {\n clearTimeout(loaderState.removeTimer);\n loaderState.removeTimer = null;\n }\n\n const overlay = document.createElement('div');\n overlay.className = 'timeline__loader-overlay';\n\n const loader = document.createElement('div');\n loader.className = 'timeline__loader';\n\n const spinner = document.createElement('img');\n spinner.src = timelineBasePath + '/spinner.gif';\n spinner.alt = 'Loading...';\n spinner.title = 'Loading...';\n spinner.className = 'timeline__loader-spinner';\n // Default to a compact spinner if page CSS is missing\n spinner.width = 120;\n spinner.style.height = 'auto';\n\n loader.appendChild(spinner);\n overlay.appendChild(loader);\n\n document.body.appendChild(overlay);\n loaderState.overlayEl = overlay;\n}\n\nexport function hideTimelineLoader() {\n if (loaderState.count <= 0) return;\n loaderState.count -= 1;\n if (loaderState.count > 0) return;\n\n const elapsed = Date.now() - loaderState.startTime;\n const minMs = getTimelineLoaderMinMs();\n const remaining = Math.max(0, minMs - elapsed);\n\n const removeOverlay = function() {\n if (loaderState.overlayEl) {\n loaderState.overlayEl.remove();\n loaderState.overlayEl = null;\n }\n loaderState.removeTimer = null;\n };\n\n if (loaderState.removeTimer) {\n clearTimeout(loaderState.removeTimer);\n loaderState.removeTimer = null;\n }\n\n if (remaining > 0) {\n loaderState.removeTimer = setTimeout(removeOverlay, remaining);\n } else {\n removeOverlay();\n }\n}\n","import { timelineBasePath } from '../shared/config.js';\nimport { hideTimelineLoader } from './loader-ui.js';\n\nexport function showTimelineError(container, errorType, details) {\n if (!container) return;\n\n const errorMessages = {\n 'json-load': {\n title: 'Timeline Data Could Not Be Loaded',\n message: 'The timeline data failed to load. This could be due to a network error or an incorrect file path.',\n solution: 'Please check that the data-json-config path is correct and the file is accessible.'\n },\n 'json-parse': {\n title: 'Invalid Timeline Data',\n message: 'The timeline data file exists but contains invalid JSON.',\n solution: 'Please validate your JSON using a tool like jsonlint.com and ensure it follows the correct schema.'\n },\n 'missing-element': {\n title: 'Timeline Element Not Found',\n message: 'The required timeline container element could not be found on the page.',\n solution: 'Ensure your HTML includes a container with the class \"timeline\" and the correct selector.'\n },\n 'invalid-config': {\n title: 'Invalid Configuration',\n message: 'One or more timeline configuration options are invalid.',\n solution: 'Check your data attributes or JavaScript options and ensure they match the expected format.'\n }\n };\n\n const errorInfo = errorMessages[errorType] || {\n title: 'Timeline Error',\n message: 'An unexpected error occurred while initializing the timeline.',\n solution: 'Please check the browser console for more details.'\n };\n\n hideTimelineLoader(container);\n container.innerHTML = '';\n\n const errorDiv = document.createElement('div');\n errorDiv.className = 'timeline__error';\n\n const errorIcon = document.createElement('img');\n errorIcon.src = timelineBasePath + '/alert.svg';\n errorIcon.alt = 'Error';\n errorIcon.className = 'timeline__error-icon';\n // Ensure a reasonable default size even if CSS isn't loaded\n // This makes the error icon usable when a consumer forgets to include CSS.\n errorIcon.width = 200;\n errorIcon.style.height = 'auto';\n\n const errorTitle = document.createElement('h2');\n errorTitle.className = 'timeline__error-title';\n errorTitle.textContent = errorInfo.title;\n\n const errorMessage = document.createElement('p');\n errorMessage.className = 'timeline__error-message';\n errorMessage.textContent = errorInfo.message;\n\n const errorSolution = document.createElement('p');\n errorSolution.className = 'timeline__error-solution';\n errorSolution.innerHTML = '<strong>Solution:</strong> ' + errorInfo.solution;\n\n if (details) {\n const errorDetails = document.createElement('p');\n errorDetails.className = 'timeline__error-details';\n errorDetails.innerHTML = '<strong>Details:</strong> ' + details;\n errorDiv.appendChild(errorDetails);\n }\n\n errorDiv.appendChild(errorIcon);\n errorDiv.appendChild(errorTitle);\n errorDiv.appendChild(errorMessage);\n errorDiv.appendChild(errorSolution);\n\n container.appendChild(errorDiv);\n\n console.error('Timeline Error [' + errorType + ']:', errorInfo.message, details || '');\n}\n","// Utility helpers shared across modules\nexport function getContrastColor(bgColor) {\n const brightness = getColorBrightness(bgColor);\n return brightness > 128 ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.3)';\n}\n\nexport function getColorBrightness(color) {\n let rgb;\n if (!color || typeof color !== 'string') return 128;\n\n if (color.startsWith('#')) {\n let hex = color.substring(1);\n if (hex.length === 3) {\n hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n }\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n rgb = [r, g, b];\n } else if (color.startsWith('rgb')) {\n const matches = color.match(/\\d+/g);\n rgb = matches ? matches.map(Number) : [128, 128, 128];\n } else {\n return 128;\n }\n\n return (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;\n}\n","import { getContrastColor, getColorBrightness } from '../shared/utils.js';\n\nexport function applyTimelineColors(container, config) {\n let nodeColor = config.nodeColor || null;\n let lineColor = config.lineColor || null;\n const navColor = config.navColor || null;\n\n if (nodeColor && !lineColor) lineColor = nodeColor;\n if (lineColor && !nodeColor) nodeColor = lineColor;\n\n if (nodeColor) {\n container.style.setProperty('--timeline-node-color', nodeColor);\n }\n if (lineColor) {\n container.style.setProperty('--timeline-line-color', lineColor);\n }\n if (navColor) {\n container.style.setProperty('--timeline-nav-color', navColor);\n container.style.setProperty('--timeline-nav-border', getContrastColor(navColor));\n const brightness = getColorBrightness(navColor);\n const arrowColor = brightness > 128 ? '#333' : '#fff';\n container.style.setProperty('--timeline-arrow-color', arrowColor);\n container.setAttribute('data-arrow-color', arrowColor);\n }\n}\n","import { showTimelineLoader, hideTimelineLoader } from '../features/loader-ui.js';\nimport { showTimelineError } from '../features/error-ui.js';\nimport { applyTimelineColors } from '../features/colors.js';\nimport { openTimelineModal } from '../features/modals.js';\nimport { timelineRegistry } from '../shared/state.js';\n\nfunction ensureInlineModalData(itemEl) {\n if (!itemEl) return;\n const content = itemEl.querySelector('.timeline__content') || itemEl;\n if (!itemEl.hasAttribute('data-modal-title')) {\n const heading = content.querySelector('h1,h2,h3,h4,h5,h6');\n if (heading && heading.textContent) {\n itemEl.setAttribute('data-modal-title', heading.textContent.trim());\n }\n }\n if (!itemEl.hasAttribute('data-modal-content')) {\n const firstP = content.querySelector('p');\n if (firstP && firstP.textContent) {\n itemEl.setAttribute('data-modal-content', firstP.textContent.trim());\n }\n }\n if (!itemEl.hasAttribute('data-modal-image')) {\n const img = content.querySelector('img');\n if (img && img.getAttribute('src')) {\n itemEl.setAttribute('data-modal-image', img.getAttribute('src'));\n }\n }\n}\n\nfunction enhanceInlineItems(timelineEl, items) {\n if (!items || !items.length) return;\n items.forEach(function(item){\n if (item.getAttribute('data-modal-bound') === '1') return;\n ensureInlineModalData(item);\n const hasModal = item.hasAttribute('data-modal-title') || item.hasAttribute('data-modal-content') || item.hasAttribute('data-modal-image') || item.hasAttribute('data-modal-html');\n if (hasModal) {\n item.addEventListener('click', function(e){\n e.preventDefault();\n openTimelineModal(item);\n });\n item.setAttribute('data-modal-bound', '1');\n }\n });\n}\n\nfunction createArrowSVG(direction, color) {\n if (direction === 'left') {\n return '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"7.8\" height=\"14\" style=\"display:block;margin:auto;\"><path fill=\"none\" stroke=\"' + color + '\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6.8 1L1 7l5.8 6\"/></svg>';\n }\n return '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"7.8\" height=\"14\" style=\"display:block;margin:auto;\"><path fill=\"none\" stroke=\"' + color + '\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M1 1l5.8 6L1 13\"/></svg>';\n}\n\nexport function timeline(collection, options) {\n const timelines = [];\n const warningLabel = 'Timeline:';\n let winWidth = window.innerWidth;\n let resizeTimer;\n let currentIndex = 0;\n const eventListeners = new Map(); // Track listeners for cleanup\n\n showTimelineLoader();\n let shouldHideLoader = true;\n\n const defaultSettings = {\n minWidth: { type: 'integer', defaultValue: 600 },\n maxWidth: { type: 'integer', defaultValue: 600 },\n horizontalStartPosition: { type: 'string', acceptedValues: ['bottom', 'top'], defaultValue: 'top' },\n mode: { type: 'string', acceptedValues: ['horizontal', 'vertical'], defaultValue: 'vertical' },\n moveItems: { type: 'integer', defaultValue: 1 },\n rtlMode: { type: 'boolean', acceptedValues: [true, false], defaultValue: false },\n startIndex: { type: 'integer', defaultValue: 0 },\n verticalStartPosition: { type: 'string', acceptedValues: ['left', 'right'], defaultValue: 'left' },\n verticalTrigger: { type: 'string', defaultValue: '15%' },\n visibleItems: { type: 'integer', defaultValue: 3 }\n };\n\n function testValues(value, settingName) {\n if (typeof value !== 'number' && value % 1 !== 0) {\n console.warn(`${warningLabel} The value \"${value}\" entered for the setting \"${settingName}\" is not an integer.`);\n return false;\n }\n return true;\n }\n\n function itemWrap(el, wrapper, classes) {\n wrapper.classList.add(classes);\n el.parentNode.insertBefore(wrapper, el);\n wrapper.appendChild(el);\n }\n\n function wrapElements(items) {\n items.forEach((item) => {\n itemWrap(item.querySelector('.timeline__content'), document.createElement('div'), 'timeline__content__wrap');\n itemWrap(item.querySelector('.timeline__content__wrap'), document.createElement('div'), 'timeline__item__inner');\n });\n }\n\n function isElementInViewport(el, triggerPosition) {\n const rect = el.getBoundingClientRect();\n const windowHeight = window.innerHeight || document.documentElement.clientHeight;\n const defaultTrigger = defaultSettings.verticalTrigger.defaultValue.match(/(\\d*\\.?\\d*)(.*)/);\n let triggerUnit = triggerPosition.unit;\n let triggerValue = triggerPosition.value;\n let trigger = windowHeight;\n if (triggerUnit === 'px' && triggerValue >= windowHeight) {\n console.warn('The value entered for the setting \"verticalTrigger\" is larger than the window height. The default value will be used instead.');\n [, triggerValue, triggerUnit] = defaultTrigger;\n }\n if (triggerUnit === 'px') {\n trigger = parseInt(trigger - triggerValue, 10);\n } else if (triggerUnit === '%') {\n trigger = parseInt(trigger * ((100 - triggerValue) / 100), 10);\n }\n return (\n rect.top <= trigger &&\n rect.left <= (window.innerWidth || document.documentElement.clientWidth) &&\n (rect.top + rect.height) >= 0 &&\n (rect.left + rect.width) >= 0\n );\n }\n\n function addTransforms(el, transform) {\n el.style.webkitTransform = transform;\n el.style.msTransform = transform;\n el.style.transform = transform;\n }\n\n function createTimelines(timelineEl) {\n const timelineName = timelineEl.id ? `#${timelineEl.id}` : `.${timelineEl.className}`;\n const errorPart = 'could not be found as a direct descendant of';\n const data = timelineEl.dataset;\n let wrap;\n let scroller;\n let items;\n const settings = {};\n\n try {\n wrap = timelineEl.querySelector('.timeline__wrap');\n if (!wrap) {\n throw new Error(`${warningLabel} .timeline__wrap ${errorPart} ${timelineName}`);\n } else {\n scroller = wrap.querySelector('.timeline__items');\n if (!scroller) {\n throw new Error(`${warningLabel} .timeline__items ${errorPart} .timeline__wrap`);\n } else {\n items = [].slice.call(scroller.children, 0);\n }\n }\n } catch (e) {\n console.warn(e.message);\n showTimelineError(timelineEl, 'missing-element', e.message);\n return false;\n }\n\n Object.keys(defaultSettings).forEach((key) => {\n settings[key] = defaultSettings[key].defaultValue;\n\n if (key === 'minWidth') {\n let candidate = undefined;\n if (data.minWidth !== undefined) candidate = data.minWidth;\n if (data.minwidth !== undefined) candidate = data.minwidth;\n if (data.forceVerticalMode !== undefined) candidate = data.forceVerticalMode;\n if (data.forceverticalmode !== undefined) candidate = data.forceverticalmode;\n if (candidate === undefined && options) {\n if (options.minWidth !== undefined) candidate = options.minWidth;\n else if (options.forceVerticalMode !== undefined) candidate = options.forceVerticalMode;\n }\n if (candidate !== undefined) settings.minWidth = candidate;\n } else if (key === 'maxWidth') {\n let candidate = undefined;\n if (data.maxWidth !== undefined) candidate = data.maxWidth;\n if (data.maxwidth !== undefined) candidate = data.maxwidth;\n if (candidate === undefined && options) {\n if (options.maxWidth !== undefined) candidate = options.maxWidth;\n }\n if (candidate !== undefined) settings.maxWidth = candidate;\n } else {\n if (data[key]) {\n settings[key] = data[key];\n } else if (options && options[key] !== undefined) {\n settings[key] = options[key];\n }\n }\n\n if (defaultSettings[key].type === 'integer') {\n if (!settings[key] || !testValues(settings[key], key)) {\n settings[key] = defaultSettings[key].defaultValue;\n }\n } else if (defaultSettings[key].type === 'string') {\n if (defaultSettings[key].acceptedValues && defaultSettings[key].acceptedValues.indexOf(settings[key]) === -1) {\n console.warn(`${warningLabel} The value \"${settings[key]}\" entered for the setting \"${key}\" was not recognised.`);\n settings[key] = defaultSettings[key].defaultValue;\n }\n }\n });\n\n (function applyColorParity(){\n const data = timelineEl.dataset;\n const getData = function(k){\n return data[k] !== undefined ? data[k] : (data[k && k.toLowerCase()] !== undefined ? data[k.toLowerCase()] : undefined);\n };\n let nodeColor = getData('nodeColor');\n let lineColor = getData('lineColor');\n let navColor = getData('navColor');\n if (options) {\n if (options.nodeColor !== undefined) nodeColor = options.nodeColor;\n if (options.lineColor !== undefined) lineColor = options.lineColor;\n if (options.navColor !== undefined) navColor = options.navColor;\n }\n if (nodeColor || lineColor || navColor) {\n applyTimelineColors(timelineEl, { nodeColor, lineColor, navColor });\n }\n })();\n\n const defaultTrigger = defaultSettings.verticalTrigger.defaultValue.match(/(\\d*\\.?\\d*)(.*)/);\n const triggerArray = settings.verticalTrigger.match(/(\\d*\\.?\\d*)(.*)/);\n let [, triggerValue, triggerUnit] = triggerArray;\n let triggerValid = true;\n if (!triggerValue) {\n console.warn(`${warningLabel} No numercial value entered for the 'verticalTrigger' setting.`);\n triggerValid = false;\n }\n if (triggerUnit !== 'px' && triggerUnit !== '%') {\n console.warn(`${warningLabel} The setting 'verticalTrigger' must be a percentage or pixel value.`);\n triggerValid = false;\n }\n if (triggerUnit === '%' && (triggerValue > 100 || triggerValue < 0)) {\n console.warn(`${warningLabel} The 'verticalTrigger' setting value must be between 0 and 100 if using a percentage value.`);\n triggerValid = false;\n } else if (triggerUnit === 'px' && triggerValue < 0) {\n console.warn(`${warningLabel} The 'verticalTrigger' setting value must be above 0 if using a pixel value.`);\n triggerValid = false;\n }\n\n if (triggerValid === false) {\n [, triggerValue, triggerUnit] = defaultTrigger;\n }\n\n settings.verticalTrigger = {\n unit: triggerUnit,\n value: triggerValue\n };\n\n if (settings.moveItems > settings.visibleItems) {\n console.warn(`${warningLabel} The value of \"moveItems\" (${settings.moveItems}) is larger than the number of \"visibleItems\" (${settings.visibleItems}). The value of \"visibleItems\" has been used instead.`);\n settings.moveItems = settings.visibleItems;\n }\n\n if (settings.startIndex > (items.length - settings.visibleItems) && items.length > settings.visibleItems) {\n console.warn(`${warningLabel} The 'startIndex' setting must be between 0 and ${items.length - settings.visibleItems} for this timeline. The value of ${items.length - settings.visibleItems} has been used instead.`);\n settings.startIndex = items.length - settings.visibleItems;\n } else if (items.length <= settings.visibleItems) {\n console.warn(`${warningLabel} The number of items in the timeline must exceed the number of visible items to use the 'startIndex' option.`);\n settings.startIndex = 0;\n } else if (settings.startIndex < 0) {\n console.warn(`${warningLabel} The 'startIndex' setting must be between 0 and ${items.length - settings.visibleItems} for this timeline. The value of 0 has been used instead.`);\n settings.startIndex = 0;\n }\n\n enhanceInlineItems(timelineEl, items);\n\n if (!timelineEl.id) {\n timelineEl.setAttribute('data-timeline-id', 'timeline-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9));\n }\n\n timelines.push({\n timelineEl,\n wrap,\n scroller,\n items,\n settings,\n listeners: [] // Store listeners for cleanup\n });\n }\n\n if (collection.length) {\n Array.from(collection).forEach(createTimelines);\n }\n\n function setHeightandWidths(tl) {\n function setWidths() {\n tl.itemWidth = 200;\n tl.items.forEach((item) => {\n item.style.width = `${tl.itemWidth}px`;\n });\n tl.scrollerWidth = tl.itemWidth * tl.items.length;\n tl.scroller.style.width = `${tl.scrollerWidth}px`;\n }\n\n function setHeights() {\n let oddIndexTallest = 0;\n let evenIndexTallest = 0;\n tl.items.forEach((item, i) => {\n item.style.height = 'auto';\n const height = item.offsetHeight;\n if (i % 2 === 0) {\n evenIndexTallest = height > evenIndexTallest ? height : evenIndexTallest;\n } else {\n oddIndexTallest = height > oddIndexTallest ? height : oddIndexTallest;\n }\n });\n\n const transformString = `translateY(${evenIndexTallest}px)`;\n tl.items.forEach((item, i) => {\n if (i % 2 === 0) {\n item.style.height = `${evenIndexTallest}px`;\n if (tl.settings.horizontalStartPosition === 'bottom') {\n item.classList.add('timeline__item--bottom');\n addTransforms(item, transformString);\n } else {\n item.classList.add('timeline__item--top');\n }\n } else {\n item.style.height = `${oddIndexTallest}px`;\n if (tl.settings.horizontalStartPosition !== 'bottom') {\n item.classList.add('timeline__item--bottom');\n addTransforms(item, transformString);\n } else {\n item.classList.add('timeline__item--top');\n }\n }\n });\n tl.scroller.style.height = `${evenIndexTallest + oddIndexTallest}px`;\n }\n\n if (window.innerWidth > tl.settings.minWidth) {\n setWidths();\n setHeights();\n }\n }\n\n function addNavigation(tl) {\n const viewportWidth = tl.wrap.offsetWidth;\n const itemsVisible = Math.floor(viewportWidth / tl.itemWidth);\n\n if (tl.items.length > itemsVisible) {\n const prevArrow = document.createElement('button');\n const nextArrow = document.createElement('button');\n const topPosition = tl.items[0].offsetHeight;\n prevArrow.className = 'timeline-nav-button timeline-nav-button--prev';\n nextArrow.className = 'timeline-nav-button timeline-nav-button--next';\n prevArrow.textContent = 'Previous';\n nextArrow.textContent = 'Next';\n prevArrow.style.top = `${topPosition}px`;\n nextArrow.style.top = `${topPosition}px`;\n\n const arrowColor = tl.timelineEl.getAttribute('data-arrow-color') || '#333';\n prevArrow.innerHTML = createArrowSVG('left', arrowColor);\n nextArrow.innerHTML = createArrowSVG('right', arrowColor);\n\n const maxIndex = Math.max(0, tl.items.length - itemsVisible);\n if (currentIndex === 0) {\n prevArrow.disabled = true;\n } else if (currentIndex >= maxIndex) {\n nextArrow.disabled = true;\n }\n tl.timelineEl.appendChild(prevArrow);\n tl.timelineEl.appendChild(nextArrow);\n }\n }\n\n function addHorizontalDivider(tl) {\n const divider = tl.timelineEl.querySelector('.timeline-divider');\n if (divider) {\n tl.timelineEl.removeChild(divider);\n }\n const topPosition = tl.items[0].offsetHeight;\n const horizontalDivider = document.createElement('span');\n horizontalDivider.className = 'timeline-divider';\n horizontalDivider.style.top = `${topPosition}px`;\n tl.timelineEl.appendChild(horizontalDivider);\n }\n\n function timelinePosition(tl) {\n const position = tl.items[currentIndex].offsetLeft;\n const str = `translate3d(-${position}px, 0, 0)`;\n addTransforms(tl.scroller, str);\n }\n\n function slideTimeline(tl) {\n const navArrows = tl.timelineEl.querySelectorAll('.timeline-nav-button');\n const arrowPrev = tl.timelineEl.querySelector('.timeline-nav-button--prev');\n const arrowNext = tl.timelineEl.querySelector('.timeline-nav-button--next');\n\n const viewportWidth = tl.wrap.offsetWidth;\n const itemsVisible = Math.floor(viewportWidth / tl.itemWidth);\n const maxIndex = Math.max(0, tl.items.length - itemsVisible);\n\n const moveItems = parseInt(tl.settings.moveItems, 10);\n \n const handleArrowClick = function(e) {\n e.preventDefault();\n e.stopPropagation();\n\n if (this.disabled) {\n return;\n }\n\n currentIndex = this.classList.contains('timeline-nav-button--next') ? (currentIndex += moveItems) : (currentIndex -= moveItems);\n if (currentIndex === 0 || currentIndex < 0) {\n currentIndex = 0;\n arrowPrev.disabled = true;\n arrowNext.disabled = false;\n } else if (currentIndex === maxIndex || currentIndex > maxIndex) {\n currentIndex = maxIndex;\n arrowPrev.disabled = false;\n arrowNext.disabled = true;\n } else {\n arrowPrev.disabled = false;\n arrowNext.disabled = false;\n }\n timelinePosition(tl);\n };\n \n Array.from(navArrows).forEach((arrow) => {\n arrow.addEventListener('click', handleArrowClick);\n tl.listeners.push({ element: arrow, type: 'click', handler: handleArrowClick });\n });\n }\n\n function setUpHorinzontalTimeline(tl) {\n if (tl.settings.rtlMode) {\n currentIndex = tl.items.length > tl.settings.visibleItems ? tl.items.length - tl.settings.visibleItems : 0;\n } else {\n currentIndex = tl.settings.startIndex;\n }\n tl.timelineEl.classList.add('timeline--horizontal');\n setHeightandWidths(tl);\n timelinePosition(tl);\n addNavigation(tl);\n addHorizontalDivider(tl);\n slideTimeline(tl);\n\n const timelineId = tl.timelineEl.id || tl.timelineEl.getAttribute('data-timeline-id');\n if (timelineId) {\n timelineRegistry[timelineId] = {\n setCurrentIndex: function(index) {\n const viewportWidth = tl.wrap.offsetWidth;\n const itemsVisible = Math.floor(viewportWidth / tl.itemWidth);\n const maxIndex = Math.max(0, tl.items.length - itemsVisible);\n currentIndex = Math.max(0, Math.min(index, maxIndex));\n },\n updatePosition: function() {\n timelinePosition(tl);\n const arrowPrev = tl.timelineEl.querySelector('.timeline-nav-button--prev');\n const arrowNext = tl.timelineEl.querySelector('.timeline-nav-button--next');\n if (arrowPrev && arrowNext) {\n const viewportWidth = tl.wrap.offsetWidth;\n const itemsVisible = Math.floor(viewportWidth / tl.itemWidth);\n const maxIndex = Math.max(0, tl.items.length - itemsVisible);\n arrowPrev.disabled = currentIndex === 0;\n arrowNext.disabled = currentIndex >= maxIndex;\n }\n }\n };\n }\n }\n\n function setUpVerticalTimeline(tl) {\n let lastVisibleIndex = 0;\n tl.items.forEach((item, i) => {\n item.classList.remove('animated', 'fadeIn');\n if (!isElementInViewport(item, tl.settings.verticalTrigger) && i > 0) {\n item.classList.add('animated');\n } else {\n lastVisibleIndex = i;\n }\n const divider = tl.settings.verticalStartPosition === 'left' ? 1 : 0;\n if (i % 2 === divider && window.innerWidth > tl.settings.minWidth) {\n item.classList.add('timeline__item--right');\n } else {\n item.classList.add('timeline__item--left');\n }\n });\n for (let i = 0; i < lastVisibleIndex; i += 1) {\n tl.items[i].classList.remove('animated', 'fadeIn');\n }\n \n // Use IntersectionObserver instead of scroll listener for better performance\n if ('IntersectionObserver' in window) {\n const observerOptions = {\n rootMargin: tl.settings.verticalTrigger.unit === '%' \n ? `${tl.settings.verticalTrigger.value}%` \n : `${tl.settings.verticalTrigger.value}px`,\n threshold: 0.01\n };\n \n const observer = new IntersectionObserver((entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n entry.target.classList.add('fadeIn');\n }\n });\n }, observerOptions);\n \n tl.items.forEach((item) => {\n if (item.classList.contains('animated')) {\n observer.observe(item);\n }\n });\n \n // Store observer for cleanup\n tl.observer = observer;\n } else {\n // Fallback for older browsers (though we're targeting 2018+)\n const scrollHandler = () => {\n tl.items.forEach((item) => {\n if (isElementInViewport(item, tl.settings.verticalTrigger)) {\n item.classList.add('fadeIn');\n }\n });\n };\n window.addEventListener('scroll', scrollHandler);\n tl.listeners.push({ element: window, type: 'scroll', handler: scrollHandler });\n }\n }\n\n function resetTimelines(tl) {\n // Clean up event listeners\n if (tl.listeners && tl.listeners.length > 0) {\n tl.listeners.forEach(({ element, type, handler }) => {\n element.removeEventListener(type, handler);\n });\n tl.listeners = [];\n }\n \n // Clean up IntersectionObserver\n if (tl.observer) {\n tl.observer.disconnect();\n tl.observer = null;\n }\n \n tl.timelineEl.classList.remove('timeline--horizontal', 'timeline--mobile');\n tl.scroller.removeAttribute('style');\n tl.items.forEach((item) => {\n item.removeAttribute('style');\n item.classList.remove('animated', 'fadeIn', 'timeline__item--left', 'timeline__item--right');\n });\n const navArrows = tl.timelineEl.querySelectorAll('.timeline-nav-button');\n Array.from(navArrows).forEach((arrow) => {\n arrow.parentNode.removeChild(arrow);\n });\n }\n\n function setUpTimelines() {\n timelines.forEach((tl) => {\n tl.timelineEl.style.opacity = 0;\n if (!tl.timelineEl.classList.contains('timeline--loaded')) {\n wrapElements(tl.items);\n }\n resetTimelines(tl);\n if (window.innerWidth <= tl.settings.minWidth) {\n tl.timelineEl.classList.add('timeline--mobile');\n }\n \n // Determine which mode to use based on settings and viewport width\n let useHorizontalMode = false;\n if (tl.settings.mode === 'horizontal' && window.innerWidth > tl.settings.minWidth) {\n useHorizontalMode = true;\n } else if (tl.settings.mode === 'vertical' && window.innerWidth > tl.settings.maxWidth) {\n useHorizontalMode = true;\n }\n \n if (useHorizontalMode) {\n setUpHorinzontalTimeline(tl);\n } else {\n setUpVerticalTimeline(tl);\n }\n tl.timelineEl.classList.add('timeline--loaded');\n });\n\n setTimeout(() => {\n timelines.forEach((tl) => {\n tl.timelineEl.style.opacity = 1;\n });\n }, 500);\n\n if (shouldHideLoader) {\n hideTimelineLoader();\n shouldHideLoader = false;\n }\n }\n\n setUpTimelines();\n\n window.addEventListener('resize', () => {\n clearTimeout(resizeTimer);\n resizeTimer = setTimeout(() => {\n const newWinWidth = window.innerWidth;\n if (newWinWidth !== winWidth) {\n setUpTimelines();\n winWidth = newWinWidth;\n }\n }, 250);\n });\n}","import { timelineBasePath } from '../shared/config.js';\nimport { showTimelineLoader, hideTimelineLoader } from './loader-ui.js';\nimport { showTimelineError } from './error-ui.js';\nimport { applyTimelineColors } from './colors.js';\nimport { handleDeepLinking } from './deep-linking.js';\nimport { timeline } from '../core/timeline-engine.js';\n\nfunction createItemNode(item) {\n const itemEl = document.createElement('div');\n itemEl.className = 'timeline__item';\n\n if (item.id) {\n itemEl.setAttribute('data-node-id', item.id);\n }\n\n itemEl.setAttribute('data-modal-title', item.title || '');\n itemEl.setAttribute('data-modal-content', item.content || '');\n itemEl.setAttribute('data-modal-image', item.image || '');\n if (item.html) {\n itemEl.setAttribute('data-modal-html', item.html);\n }\n\n const content = document.createElement('div');\n content.className = 'timeline__content';\n\n if (item.image) {\n const img = document.createElement('img');\n img.src = item.image;\n img.className = 'timeline__image';\n img.alt = item.title || '';\n img.onerror = function() {\n console.error('Timeline: The image \"' + item.image + '\" could not be loaded. Please check the path.');\n this.src = timelineBasePath + '/missing-image.svg';\n this.alt = 'Image not found';\n this.title = 'Original image: ' + item.image;\n };\n content.appendChild(img);\n }\n\n const textWrapper = document.createElement('div');\n if (item.title) {\n const title = document.createElement('h3');\n title.textContent = item.title;\n textWrapper.appendChild(title);\n }\n\n if (item.content) {\n const para = document.createElement('p');\n let displayText = item.content;\n if (displayText.length > 105) {\n displayText = displayText.substring(0, 105) + '...';\n }\n para.innerHTML = displayText;\n textWrapper.appendChild(para);\n }\n\n if (item.html) {\n const wrapper = document.createElement('div');\n wrapper.innerHTML = item.html;\n textWrapper.appendChild(wrapper);\n }\n\n content.appendChild(textWrapper);\n itemEl.appendChild(content);\n\n itemEl.addEventListener('click', function(e) {\n e.preventDefault();\n if (typeof window.openTimelineModal === 'function') {\n window.openTimelineModal(itemEl);\n }\n });\n itemEl.setAttribute('data-modal-bound', '1');\n\n return itemEl;\n}\n\nfunction applyDataAttributes(container, config) {\n if (config.layoutMode) {\n container.setAttribute('data-mode', config.layoutMode);\n }\n if (config.visibleItems !== undefined) {\n container.setAttribute('data-visible-items', config.visibleItems);\n }\n if (config.minWidth !== undefined) {\n container.setAttribute('data-min-width', config.minWidth);\n container.setAttribute('data-force-vertical-mode', config.minWidth);\n }\n if (config.maxWidth !== undefined) {\n container.setAttribute('data-max-width', config.maxWidth);\n }\n}\n\nexport function renderTimelineFromData(containerSelector, data, config) {\n const container = document.querySelector(containerSelector);\n if (!container) return;\n\n let itemsWrap = container.querySelector('.timeline__items');\n if (!itemsWrap) {\n const wrap = document.createElement('div');\n wrap.className = 'timeline__wrap';\n itemsWrap = document.createElement('div');\n itemsWrap.className = 'timeline__items';\n wrap.appendChild(itemsWrap);\n container.appendChild(wrap);\n } else {\n itemsWrap.innerHTML = '';\n }\n\n if (config) {\n applyDataAttributes(container, config);\n applyTimelineColors(container, config);\n\n if (config.timelineName && config.timelineName.trim() !== '') {\n const existingHeading = container.previousElementSibling;\n if (existingHeading && existingHeading.classList.contains('timeline__heading')) {\n existingHeading.textContent = config.timelineName;\n } else {\n const heading = document.createElement('h1');\n heading.className = 'timeline__heading';\n heading.textContent = config.timelineName;\n container.parentNode.insertBefore(heading, container);\n }\n container.setAttribute('data-timeline-name', config.timelineName);\n }\n }\n\n data.forEach(function (it) {\n itemsWrap.appendChild(createItemNode(it));\n });\n\n return container;\n}\n\nexport function timelineFromData(containerSelector, data, options) {\n const container = renderTimelineFromData(containerSelector, data, options);\n if (!container) return;\n timeline([container], options);\n}\n\nexport function loadDataFromJson(url, containerSelector) {\n const container = document.querySelector(containerSelector);\n if (!container) {\n console.error('Timeline: Container not found:', containerSelector);\n return;\n }\n\n showTimelineLoader();\n\n const timelineId = container ? container.id : null;\n const cacheKey = timelineId ? 'vjs_' + timelineId : null;\n\n if (cacheKey && typeof(Storage) !== 'undefined') {\n try {\n const cached = localStorage.getItem(cacheKey);\n if (cached) {\n const cachedData = JSON.parse(cached);\n fetch(url).then(function(res) {\n if (!res.ok) throw new Error('Failed to load ' + url + ' (' + res.status + ')');\n return res.json();\n }).then(function(json) {\n if (json.lastupdated && cachedData.lastupdated && json.lastupdated === cachedData.lastupdated) {\n console.log('Using cached timeline data for', timelineId);\n processTimelineData(cachedData, containerSelector);\n } else {\n console.log('Updating cached timeline data for', timelineId);\n localStorage.setItem(cacheKey, JSON.stringify(json));\n processTimelineData(json, containerSelector);\n }\n }).catch(function(err) {\n console.warn('Failed to fetch fresh data, using cache:', err);\n processTimelineData(cachedData, containerSelector);\n });\n return;\n }\n } catch (e) {\n console.warn('Error reading from localStorage:', e);\n }\n }\n\n fetch(url).then(function (res) {\n if (!res.ok) throw new Error('Failed to load ' + url + ' (' + res.status + ')');\n return res.json();\n }).then(function (json) {\n if (cacheKey && typeof(Storage) !== 'undefined') {\n try {\n localStorage.setItem(cacheKey, JSON.stringify(json));\n console.log('Cached timeline data for', timelineId);\n } catch (e) {\n console.warn('Failed to cache timeline data:', e);\n }\n }\n processTimelineData(json, containerSelector);\n }).catch(function (err) {\n console.error('Error loading timeline JSON:', err);\n showTimelineError(container, 'json-load', err.message);\n });\n}\n\nexport function processTimelineData(json, containerSelector) {\n const container = document.querySelector(containerSelector);\n if (!container) {\n console.error('Timeline: Container not found:', containerSelector);\n return;\n }\n\n let config = null;\n let nodes = [];\n\n try {\n if (json.nodes && Array.isArray(json.nodes)) {\n nodes = json.nodes;\n config = {\n timelineName: json.timelineName,\n layoutMode: json.layoutMode,\n visibleItems: json.visibleItems,\n minWidth: json.minWidth,\n maxWidth: json.maxWidth,\n nodeColor: json.nodeColor,\n lineColor: json.lineColor,\n navColor: json.navColor,\n lastupdated: json.lastupdated\n };\n } else if (Array.isArray(json)) {\n nodes = json;\n } else {\n throw new Error('Invalid JSON format. Expected object with \"nodes\" array or simple array.');\n }\n\n if (nodes.length === 0) {\n throw new Error('No timeline items found in data.');\n }\n\n renderTimelineFromData(containerSelector, nodes, config);\n\n try {\n timeline(document.querySelectorAll(containerSelector));\n handleDeepLinking(containerSelector);\n hideTimelineLoader();\n } catch (e) {\n console.error('Error initializing timeline:', e);\n const container = document.querySelector(containerSelector);\n if (container) {\n showTimelineError(container, 'invalid-config', e.message);\n }\n hideTimelineLoader();\n }\n\n } catch (e) {\n console.error('Error processing timeline data:', e);\n showTimelineError(container, 'json-parse', e.message);\n hideTimelineLoader();\n }\n}\n\nexport function clearTimelineCache(timelineId) {\n if (typeof(Storage) === 'undefined') {\n console.warn('localStorage not supported');\n return;\n }\n\n if (timelineId) {\n const key = 'vjs_' + timelineId;\n localStorage.removeItem(key);\n console.log('Cleared cache for timeline:', timelineId);\n } else {\n const keys = Object.keys(localStorage);\n let cleared = 0;\n keys.forEach(function(key) {\n if (key.startsWith('vjs_')) {\n localStorage.removeItem(key);\n cleared++;\n }\n });\n console.log('Cleared', cleared, 'timeline cache(s)');\n }\n}\n","// --------------------------------------------------------------------------\r\n// timeline.js - a vanilla JS app to display timelines\r\n// \r\n// Created in 2018 by Mike Collins (https://github.com/squarechip/timeline)\r\n// Modified in 2026 by Ken Dawson (https://github.com/kendawson-online)\r\n// Last updated: 01/08/26\r\n//\r\n// --------------------------------------------------------------------------\r\n\r\nimport { createTimelineModal, openTimelineModal, closeTimelineModal } from './features/modals.js';\r\nimport { handleDeepLinking, navigateToNodeIndex } from './features/deep-linking.js';\r\nimport { renderTimelineFromData, timelineFromData, loadDataFromJson, processTimelineData, clearTimelineCache } from './features/data-loader.js';\r\nimport { timeline } from './core/timeline-engine.js';\r\nimport '../css/timeline.css';\r\n\r\nfunction autoInitJsonTimelines() {\r\n const timelinesWithJson = document.querySelectorAll('[data-json-config]');\r\n timelinesWithJson.forEach(function(timelineEl) {\r\n const jsonUrl = timelineEl.getAttribute('data-json-config');\r\n if (!jsonUrl) return;\r\n const className = (timelineEl.className || '').split(' ')[0];\r\n const selector = timelineEl.id ? '#' + timelineEl.id : (className ? '.' + className : null);\r\n if (selector) {\r\n loadDataFromJson(jsonUrl, selector);\r\n }\r\n });\r\n}\r\n\r\nfunction exposeGlobals() {\r\n if (typeof window === 'undefined') return;\r\n window.timeline = timeline;\r\n window.timelineFromData = timelineFromData;\r\n window.renderTimelineFromData = renderTimelineFromData;\r\n window.processTimelineData = processTimelineData;\r\n window.loadDataFromJson = loadDataFromJson;\r\n window.clearTimelineCache = clearTimelineCache;\r\n window.createTimelineModal = createTimelineModal;\r\n window.openTimelineModal = openTimelineModal;\r\n window.closeTimelineModal = closeTimelineModal;\r\n window.handleTimelineDeepLinking = handleDeepLinking;\r\n window.navigateTimelineToNodeIndex = navigateToNodeIndex;\r\n}\r\n\r\nexposeGlobals();\r\n\r\ndocument.addEventListener('DOMContentLoaded', function() {\r\n autoInitJsonTimelines();\r\n});\r\n\r\nif (typeof window !== 'undefined' && window.jQuery) {\r\n (( $) => {\r\n $.fn.timeline = function(opts) {\r\n timeline(this, opts);\r\n return this;\r\n };\r\n })(window.jQuery);\r\n}\r\n\r\nexport {\r\n timeline,\r\n timelineFromData,\r\n renderTimelineFromData,\r\n loadDataFromJson,\r\n processTimelineData,\r\n clearTimelineCache,\r\n createTimelineModal,\r\n openTimelineModal,\r\n closeTimelineModal,\r\n handleDeepLinking,\r\n navigateToNodeIndex\r\n};"],"names":["loaderState","count","startTime","removeTimer","overlayEl","modalState","modal","overlay","timelineRegistry","createTimelineModal","document","createElement","className","addEventListener","closeTimelineModal","innerHTML","closeBtn","querySelector","closeBottomBtn","e","stopPropagation","body","appendChild","key","classList","contains","openTimelineModal","itemEl","title","getAttribute","content","image","html","modalTitle","modalText","modalImage","textContent","src","alt","style","display","replace","setTimeout","add","overflow","remove","handleDeepLinking","containerSelector","urlParams","URLSearchParams","window","location","search","timelineId","get","nodeId","targetContainer","getElementById","console","warn","scrollIntoView","behavior","block","targetNode","itemIndex","Array","from","parentNode","children","indexOf","navigateToNodeIndex","container","index","id","tlData","setCurrentIndex","updatePosition","timelineBasePath","TimelineConfig","basePath","scripts","getElementsByTagName","i","length","dir","substring","lastIndexOf","showTimelineLoader","Date","now","clearTimeout","loader","spinner","width","height","hideTimelineLoader","elapsed","remaining","Math","max","removeOverlay","showTimelineError","errorType","details","errorInfo","message","solution","errorDiv","errorIcon","errorTitle","errorMessage","errorSolution","errorDetails","error","getColorBrightness","color","rgb","startsWith","hex","parseInt","matches","match","map","Number","applyTimelineColors","config","nodeColor","lineColor","navColor","setProperty","arrowColor","setAttribute","createArrowSVG","direction","timeline","collection","options","timelines","warningLabel","resizeTimer","winWidth","innerWidth","currentIndex","shouldHideLoader","defaultSettings","minWidth","type","defaultValue","maxWidth","horizontalStartPosition","acceptedValues","mode","moveItems","rtlMode","startIndex","verticalStartPosition","verticalTrigger","visibleItems","itemWrap","el","wrapper","classes","insertBefore","isElementInViewport","triggerPosition","rect","getBoundingClientRect","windowHeight","innerHeight","documentElement","clientHeight","defaultTrigger","triggerUnit","unit","triggerValue","value","trigger","top","left","clientWidth","addTransforms","transform","webkitTransform","msTransform","timelinePosition","tl","str","items","offsetLeft","scroller","setUpHorinzontalTimeline","settings","timelineEl","itemWidth","forEach","item","scrollerWidth","oddIndexTallest","evenIndexTallest","offsetHeight","transformString","setHeights","setHeightandWidths","viewportWidth","wrap","offsetWidth","itemsVisible","floor","prevArrow","nextArrow","topPosition","maxIndex","disabled","addNavigation","divider","removeChild","horizontalDivider","addHorizontalDivider","navArrows","querySelectorAll","arrowPrev","arrowNext","handleArrowClick","preventDefault","this","arrow","listeners","push","element","handler","slideTimeline","min","setUpTimelines","opacity","removeEventListener","observer","disconnect","removeAttribute","resetTimelines","useHorizontalMode","lastVisibleIndex","observerOptions","rootMargin","threshold","IntersectionObserver","entries","entry","isIntersecting","target","observe","scrollHandler","setUpVerticalTimeline","timelineName","errorPart","data","dataset","Error","slice","call","Object","keys","candidate","undefined","minwidth","forceVerticalMode","forceverticalmode","maxwidth","settingName","getData","k","toLowerCase","triggerArray","triggerValid","hasAttribute","heading","trim","firstP","img","ensureInlineModalData","enhanceInlineItems","random","toString","substr","newWinWidth","renderTimelineFromData","itemsWrap","layoutMode","applyDataAttributes","existingHeading","previousElementSibling","it","onerror","textWrapper","para","displayText","createItemNode","timelineFromData","loadDataFromJson","url","cacheKey","cached","localStorage","getItem","cachedData","JSON","parse","fetch","then","res","ok","status","json","lastupdated","log","processTimelineData","setItem","stringify","catch","err","nodes","isArray","clearTimelineCache","removeItem","cleared","handleTimelineDeepLinking","navigateTimelineToNodeIndex","jsonUrl","split","selector","jQuery","fn","opts"],"mappings":"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"}
1
+ {"version":3,"file":"timeline.min.js","sources":["../src/js/shared/state.js","../src/js/features/modals.js","../src/js/features/deep-linking.js","../src/js/shared/config.js","../src/js/features/error-ui.js","../src/js/shared/utils.js","../src/js/features/colors.js","../src/adapters/swiper-adapter.js","../src/js/features/keyboard.js","../src/js/features/layout-fallbacks.js","../src/js/core/timeline-engine.js","../src/js/features/data-loader.js","../src/js/shared/lipsum.js","../src/js/timeline.js"],"sourcesContent":["/**\n * Global state management for timeline features\n *\n * Provides shared state variables used across multiple modules.\n * All state is managed globally to ensure single instances of shared components (e.g., modal).\n */\n\n/**\n * Global modal state\n *\n * Stores references to the singleton modal and overlay DOM elements.\n * Managed by features/modals.js\n *\n * @type {Object}\n * @property {HTMLElement|null} modal - The modal dialog element\n * @property {HTMLElement|null} overlay - The modal background overlay element\n */\nexport const modalState = {\n modal: null,\n overlay: null\n};\n\n/**\n * Global timeline instances registry\n *\n * Maps timeline element IDs to their instance objects for programmatic navigation\n * and cross-timeline communication. Used by deep-linking and navigation features.\n *\n * @type {Object.<string, Object>}\n * @example\n * // After timeline initialized with id=\"my-timeline\"\n * timelineRegistry['my-timeline'] = { scrollIndex: 0, ... }\n */\nexport const timelineRegistry = {};\n","/**\n * Timeline modal popups\n *\n * Manages a singleton modal dialog for displaying detailed timeline item information.\n * Auto-populates from data-modal-* attributes or extracted heading/image/content.\n */\n\nimport { modalState } from '../shared/state.js';\n\nlet lastFocusedElement = null;\nlet escKeyHandler = null;\n\nfunction getFocusableElements(container) {\n const focusable = container ? container.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])') : [];\n return Array.from(focusable).filter(el => !el.hasAttribute('disabled'));\n}\n\nfunction trapFocus(e) {\n if (!modalState.modal || e.key !== 'Tab') return;\n\n const focusable = getFocusableElements(modalState.modal);\n if (focusable.length === 0) return;\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement;\n\n if (e.shiftKey) {\n if (active === first || !modalState.modal.contains(active)) {\n e.preventDefault();\n last.focus();\n }\n } else if (active === last || !modalState.modal.contains(active)) {\n e.preventDefault();\n first.focus();\n }\n}\n\n/**\n * Create the global modal and overlay elements\n *\n * Creates a single modal instance that is reused for all timeline items.\n * Sets up event listeners for close button, overlay click, and ESC key.\n * Safe to call multiple times (does nothing if modal already exists).\n *\n * @returns {void}\n */\nexport function createTimelineModal() {\n if (modalState.modal) return;\n\n modalState.overlay = document.createElement('div');\n modalState.overlay.className = 'timeline-modal-overlay';\n modalState.overlay.addEventListener('click', closeTimelineModal);\n\n modalState.modal = document.createElement('div');\n modalState.modal.className = 'timeline-modal';\n modalState.modal.setAttribute('role', 'dialog');\n modalState.modal.setAttribute('aria-modal', 'true');\n\n const titleId = 'timeline-modal-title';\n modalState.modal.setAttribute('aria-labelledby', titleId);\n\n modalState.modal.innerHTML = `\n <button class=\"timeline-modal__close\" aria-label=\"Close modal\" type=\"button\"></button>\n <div class=\"timeline-modal__content\">\n <img class=\"timeline-modal__image\" src=\"\" alt=\"\" loading=\"lazy\" style=\"display: none;\">\n <h2 class=\"timeline-modal__title\" id=\"${titleId}\"></h2>\n <div class=\"timeline-modal__text\"></div>\n </div>\n <div class=\"timeline-modal__footer\">\n <button class=\"timeline-modal__close-bottom\" type=\"button\">Close</button>\n </div>\n `;\n\n const closeBtn = modalState.modal.querySelector('.timeline-modal__close');\n const closeBottomBtn = modalState.modal.querySelector('.timeline-modal__close-bottom');\n closeBtn.addEventListener('click', closeTimelineModal);\n closeBottomBtn.addEventListener('click', closeTimelineModal);\n modalState.modal.addEventListener('keydown', trapFocus);\n\n modalState.modal.addEventListener('click', function(e) {\n e.stopPropagation();\n });\n\n document.body.appendChild(modalState.overlay);\n document.body.appendChild(modalState.modal);\n\n escKeyHandler = function(e) {\n if (e.key === 'Escape' && modalState.modal.classList.contains('timeline-modal-show')) {\n closeTimelineModal();\n }\n };\n document.addEventListener('keydown', escKeyHandler);\n}\n\nexport function openTimelineModal(itemEl) {\n /**\n * Open the modal and populate with data from a timeline item\n *\n * Populates modal with item data from attributes:\n * - data-modal-title: Modal heading (required)\n * - data-modal-content: Modal body text (plain text, newlines become paragraphs)\n * - data-modal-image: Modal image src\n * - data-modal-html: Modal body HTML (overrides data-modal-content)\n *\n * Falls back to extracting from DOM if attributes missing:\n * - First heading (h1-h6) → title\n * - First image → image\n * - Inline modal content div\n *\n * Adds 'timeline-modal-show' class after 10ms delay for CSS transitions.\n * Hides body scrollbar while modal is open.\n *\n * @param {HTMLElement} itemEl - Timeline item element to open modal for\n * @returns {void}\n */\n if (!modalState.modal) {\n createTimelineModal();\n }\n\n lastFocusedElement = document.activeElement;\n\n const title = itemEl.getAttribute('data-modal-title');\n const content = itemEl.getAttribute('data-modal-content');\n const image = itemEl.getAttribute('data-modal-image');\n const html = itemEl.getAttribute('data-modal-html');\n\n const modalTitle = modalState.modal.querySelector('.timeline-modal__title');\n const modalText = modalState.modal.querySelector('.timeline-modal__text');\n const modalImage = modalState.modal.querySelector('.timeline-modal__image');\n\n modalTitle.textContent = title || '';\n\n if (image) {\n modalImage.src = image;\n modalImage.alt = title || '';\n modalImage.style.display = 'block';\n } else {\n modalImage.style.display = 'none';\n }\n\n if (html) {\n modalText.innerHTML = html;\n } else {\n // If the item contains inline modal HTML (rendered by data-loader or present in markup), prefer it\n const domModal = itemEl.querySelector('.timeline__modal-content');\n if (domModal && domModal.innerHTML && domModal.innerHTML.trim() !== '') {\n modalText.innerHTML = domModal.innerHTML;\n }\n else if (content) {\n modalText.innerHTML = '<p>' + content.replace(/\\n/g, '</p><p>') + '</p>';\n } else {\n modalText.innerHTML = '';\n }\n }\n\n setTimeout(function() {\n // Check if modal still exists (defensive against cleanup race conditions)\n if (modalState.modal && modalState.overlay) {\n modalState.modal.classList.add('timeline-modal-show');\n modalState.overlay.classList.add('timeline-modal-show');\n document.body.style.overflow = 'hidden';\n\n const firstFocusable = getFocusableElements(modalState.modal)[0];\n if (firstFocusable) firstFocusable.focus();\n }\n }, 10);\n}\n\nexport function closeTimelineModal() {\n /**\n * Close the modal and restore page scroll\n *\n * Removes 'timeline-modal-show' class from modal and overlay, triggering CSS transition.\n * Restores body scrollbar visibility.\n *\n * Safe to call when modal is not open (no-op if modal doesn't exist).\n *\n * @returns {void}\n */\n if (modalState.modal) {\n modalState.modal.classList.remove('timeline-modal-show');\n modalState.overlay.classList.remove('timeline-modal-show');\n document.body.style.overflow = '';\n if (lastFocusedElement && typeof lastFocusedElement.focus === 'function') {\n lastFocusedElement.focus();\n }\n }\n}\n\nexport function destroyTimelineModal() {\n if (escKeyHandler) {\n try { document.removeEventListener('keydown', escKeyHandler); } catch (_) {}\n escKeyHandler = null;\n }\n\n if (modalState.modal) {\n try { modalState.modal.removeEventListener('keydown', trapFocus); } catch (_) {}\n try { modalState.modal.parentNode && modalState.modal.parentNode.removeChild(modalState.modal); } catch (_) {}\n }\n if (modalState.overlay) {\n try { modalState.overlay.parentNode && modalState.overlay.parentNode.removeChild(modalState.overlay); } catch (_) {}\n }\n\n modalState.modal = null;\n modalState.overlay = null;\n document.body.style.overflow = '';\n lastFocusedElement = null;\n}\n","/**\n * Deep linking support for timeline navigation\n *\n * Allows users to link directly to specific timeline items via URL parameters.\n * Implements browser back/forward history integration.\n */\n\nimport { timelineRegistry } from '../shared/state.js';\n\n/**\n * Handle deep link URL parameters and navigate to specific timeline item\n *\n * Looks for URL parameters: * - ?id=nodeId - ID of the timeline item to show\n * - ?timeline=timelineId - Specific timeline (optional, for multiple timelines)\n *\n * If found, scrolls timeline into view and highlights the item.\n *\n * @param {string} containerSelector - CSS selector for fallback timeline container\n * @returns {void}\n */\nexport function handleDeepLinking(containerSelector) {\n const urlParams = new URLSearchParams(window.location.search);\n const timelineId = urlParams.get('timeline');\n const nodeId = urlParams.get('id');\n if (!nodeId) return;\n\n let targetContainer;\n if (timelineId) {\n targetContainer = document.getElementById(timelineId);\n } else {\n targetContainer = document.querySelector(containerSelector);\n }\n\n if (!targetContainer) {\n console.warn('Timeline not found for deep linking:', timelineId || containerSelector);\n return;\n }\n\n // Avoid smooth container scrolling for vertical timelines to prevent overriding node scroll.\n const isHorizontal = targetContainer.classList.contains('timeline--horizontal');\n if (isHorizontal) {\n targetContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });\n }\n\n let targetNode = targetContainer.querySelector('[data-node-id=\"' + nodeId + '\"]');\n // Fallback: inline demos may use element id attributes (id=\"10\") instead of data-node-id\n if (!targetNode) {\n // try by element id and ensure it's a descendant of the target container\n try {\n const byId = document.getElementById(nodeId);\n if (byId && targetContainer.contains(byId)) targetNode = byId;\n } catch (e) {\n // ignore invalid id selectors\n }\n }\n\n if (targetNode) {\n setTimeout(function() {\n // Ensure only one active item is highlighted in this timeline\n targetContainer.querySelectorAll('.timeline__item--active').forEach((n) => n.classList.remove('timeline__item--active'));\n targetNode.classList.add('timeline__item--active');\n const items = Array.from(targetNode.parentNode.children || []);\n const itemIndex = items.indexOf(targetNode);\n\n // Horizontal timelines: delegate to the engine for precision scrolling\n if (targetContainer.classList.contains('timeline--horizontal')) {\n navigateToNodeIndex(targetContainer, itemIndex);\n return;\n }\n\n // Vertical timelines: prefer native anchor scrolling\n try {\n if (!targetNode.id) {\n targetNode.id = String(nodeId);\n }\n } catch (e) {\n /* ignore */\n }\n\n try {\n // Use native anchor behavior to leverage browser scroll handling\n if (window.location && window.location.hash !== '#' + String(nodeId)) {\n window.location.hash = String(nodeId);\n }\n } catch (e) {\n /* ignore */\n }\n\n // Extra guard: ensure it is visible even if hash doesn't scroll\n try {\n targetNode.scrollIntoView({ behavior: 'auto', block: 'start' });\n } catch (e) {\n /* ignore */\n }\n }, 0);\n }\n}\n\n/**\n * Navigate timeline to a specific item by index\n *\n * Scrolls the timeline to display the item at the given index and updates the URL.\n * For horizontal timelines, uses horizontal scroll. For vertical, uses IntersectionObserver.\n *\n * @param {HTMLElement} container - Timeline container element\n * @param {number} index - Zero-based index of item to navigate to\n * @returns {void}\n */\nexport function navigateToNodeIndex(container, index) {\n if (!container) return;\n const timelineId = container.id || container.getAttribute('data-timeline-id');\n if (!timelineId) {\n console.warn('Cannot navigate: timeline container has no ID');\n return;\n }\n\n const tlData = timelineRegistry[timelineId];\n if (!tlData) {\n console.warn('Timeline not found in registry:', timelineId);\n return;\n }\n\n if (!container.classList.contains('timeline--horizontal')) {\n return;\n }\n\n if (tlData.setCurrentIndex && tlData.updatePosition) {\n tlData.setCurrentIndex(index);\n tlData.updatePosition();\n }\n}\n","/**\n * Shared configuration and path resolution\n */\n\n/**\n * Resolve the base path for loading timeline assets (images, sprites, etc.)\n *\n * Auto-detects script location and maps to the correct asset directory:\n * - dist/timeline.min.js → dist/images\n * - src/js/timeline.js → src/images\n * - Custom override via window.TimelineConfig.basePath\n *\n * Fallback: ../src/images (relative to demo pages)\n *\n * @type {string}\n */\nexport const timelineBasePath = (function() {\n // Check for user override\n if (typeof window !== 'undefined' && \n window.TimelineConfig && \n window.TimelineConfig.basePath) {\n return window.TimelineConfig.basePath;\n }\n \n const scripts = document.getElementsByTagName('script');\n for (let i = 0; i < scripts.length; i++) {\n const src = scripts[i].src || '';\n if (!src) continue;\n const dir = src.substring(0, src.lastIndexOf('/'));\n if (src.indexOf('timeline.min.js') !== -1) {\n // When loading from dist, map to dist/images\n return dir + '/images';\n }\n if (src.indexOf('timeline.js') !== -1) {\n // When loading from src/js, map to src/images\n return dir.replace('/js', '/images');\n }\n }\n // Fallback relative to demo pages; most demos live under demo/**\n return '../dist/images';\n})();\n\n/**\n * Minimum duration (ms) to display loading spinner\n *\n * Even if data loads quickly, show spinner for at least this duration\n * to avoid visual flashing.\n *\n * @type {number}\n */\n","/**\n * Error handling and display for timeline\n *\n * Shows user-friendly error messages when timeline initialization fails.\n * Prevents broken DOM from affecting page layout.\n */\n\nimport { timelineBasePath } from '../shared/config.js';\n\n/**\n * Display error message in timeline container\n *\n * Replaces timeline content with an error card showing icon, title, message, and solution.\n * Supports predefined error types or custom messages.\n *\n * Error types:\n * - 'json-load': Failed to fetch JSON file (network error, CORS, 404)\n * - 'json-parse': JSON file exists but contains invalid JSON\n * - 'missing-element': Container element not found on page\n * - 'invalid-config': Configuration options out of valid range\n *\n * @param {HTMLElement|null} container - Timeline container element to show error in\n * @param {string} errorType - Error type key (see list above)\n * @param {string} [details] - Optional additional details to log to console\n * @returns {void}\n */\nexport function showTimelineError(container, errorType, details) {\n if (!container) return;\n\n const errorMessages = {\n 'json-load': {\n title: 'Timeline Data Could Not Be Loaded',\n message: 'The timeline data failed to load. This could be due to a network error or an incorrect file path.',\n solution: 'Please check that the data-json-config path is correct and the file is accessible.'\n },\n 'load-failed': {\n title: 'Timeline Data Could Not Be Loaded',\n message: 'The timeline data failed to load. This could be due to a network error or an incorrect file path.',\n solution: 'Please check that the data-json-config path is correct and the file is accessible.'\n },\n 'json-parse': {\n title: 'Invalid Timeline Data',\n message: 'The timeline data file exists but contains invalid JSON.',\n solution: 'Please validate your JSON using a tool like jsonlint.com and ensure it follows the correct schema.'\n },\n 'missing-element': {\n title: 'Timeline Element Not Found',\n message: 'The required timeline container element could not be found on the page.',\n solution: 'Ensure your HTML includes a container with the class \"timeline\" and the correct selector.'\n },\n 'invalid-config': {\n title: 'Invalid Configuration',\n message: 'One or more timeline configuration options are invalid.',\n solution: 'Check your data attributes or JavaScript options and ensure they match the expected format.'\n }\n };\n\n const errorInfo = errorMessages[errorType] || {\n title: 'Timeline Error',\n message: 'An unexpected error occurred while initializing the timeline.',\n solution: 'Please check the browser console for more details.'\n };\n\n // mark container as error state so timeline visuals (lines, nav buttons) can be hidden via CSS\n container.classList.add('timeline--error');\n // Ensure the error UI is visible even if .timeline--loaded was never set\n container.classList.add('timeline--loaded');\n container.innerHTML = '';\n\n const errorDiv = document.createElement('div');\n errorDiv.className = 'timeline__error';\n\n const errorIcon = document.createElement('img');\n errorIcon.src = timelineBasePath ? (timelineBasePath + '/alert.svg') : '';\n errorIcon.alt = 'Error';\n errorIcon.className = 'timeline__error-icon';\n errorIcon.onload = function() {\n try {\n console.info('Timeline: error icon loaded', this.naturalWidth, 'px from', this.src);\n } catch (_) {\n /* ignore */\n }\n };\n // If the icon fails to load (misconfigured base path), fall back to a built-in SVG\n errorIcon.onerror = function() {\n this.onerror = null;\n try {\n console.warn('Timeline: error icon failed to load from', this.src || '(empty src)');\n } catch (e) {\n // ignore logging errors\n }\n this.src = 'data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"200\" height=\"200\" fill=\"none\" stroke=\"%23d32f2f\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/></svg>';\n };\n // Ensure a reasonable default size even if CSS isn't loaded\n // This makes the error icon usable when a consumer forgets to include CSS.\n errorIcon.width = 200;\n errorIcon.style.height = 'auto';\n\n const errorTitle = document.createElement('h2');\n errorTitle.className = 'timeline__error-title';\n errorTitle.textContent = errorInfo.title;\n\n const errorMessage = document.createElement('p');\n errorMessage.className = 'timeline__error-message';\n errorMessage.textContent = errorInfo.message;\n\n const errorSolution = document.createElement('p');\n errorSolution.className = 'timeline__error-solution';\n errorSolution.innerHTML = '<strong>Solution:</strong> ' + errorInfo.solution;\n\n if (details) {\n const errorDetails = document.createElement('p');\n errorDetails.className = 'timeline__error-details';\n errorDetails.innerHTML = '<strong>Details:</strong> ' + details;\n errorDiv.appendChild(errorDetails);\n }\n\n errorDiv.appendChild(errorIcon);\n errorDiv.appendChild(errorTitle);\n errorDiv.appendChild(errorMessage);\n errorDiv.appendChild(errorSolution);\n\n container.appendChild(errorDiv);\n\n console.error('Timeline Error [' + errorType + ']:', errorInfo.message, details || '');\n\n // Emit a global event so host pages can react (e.g., cancel instructions UI)\n try {\n const evt = new CustomEvent('timeline:error', {\n detail: {\n type: errorType,\n message: errorInfo.message,\n details: details || null,\n containerId: container.id || null\n }\n });\n window.dispatchEvent(evt);\n } catch (e) {\n // ignore event errors in very old browsers\n }\n}\n","/**\n * Shared utility functions for color and styling calculations\n */\n\n/**\n * Get a contrasting overlay color for the given background color\n *\n * Returns a semi-transparent dark or light overlay color based on the brightness\n * of the input color to ensure sufficient contrast.\n *\n * @param {string} bgColor - CSS color value (hex, rgb, or rgba)\n * @returns {string} - 'rgba(0, 0, 0, 0.2)' for bright backgrounds, 'rgba(255, 255, 255, 0.3)' for dark\n */\nexport function getContrastColor(bgColor) {\n const brightness = getColorBrightness(bgColor);\n return brightness > 128 ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.3)';\n}\n\n/**\n * Calculate perceived brightness of a color using relative luminance\n *\n * Uses the standard formula (RGB to perceived brightness) to determine if a color\n * is light (> 128) or dark (< 128). Handles hex (#fff, #ffffff), rgb(), and rgba() formats.\n *\n * @param {string} color - CSS color value (hex, rgb, or rgba)\n * @returns {number} - Brightness value 0-255 (or 128 if input invalid)\n */\nexport function getColorBrightness(color) {\n let rgb;\n if (!color || typeof color !== 'string') return 128;\n\n if (color.startsWith('#')) {\n let hex = color.substring(1);\n if (hex.length === 3) {\n hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n }\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n rgb = [r, g, b];\n } else if (color.startsWith('rgb')) {\n const matches = color.match(/\\d+/g);\n rgb = matches ? matches.map(Number) : [128, 128, 128];\n } else {\n return 128;\n }\n\n return (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;\n}\n\n/**\n * Format a date string into a human-friendly, locale-aware label.\n * If parsing fails, returns the original input.\n * Example output: \"October 23rd, 2023\"\n *\n * @param {string} dateString\n * @param {string} [locale=navigator.language]\n * @returns {string}\n */\nexport function formatAccessibleDate(dateString, locale = (typeof navigator !== 'undefined' && navigator.language) || 'en-US') {\n if (!dateString || typeof dateString !== 'string') return String(dateString);\n const parsed = Date.parse(dateString);\n if (Number.isNaN(parsed)) {\n // Try common MM/DD/YYYY or YYYY-MM-DD heuristics\n const parts = dateString.split(/[\\/\\.\\-]/).map(p => p.trim());\n if (parts.length === 3) {\n // Assume MM/DD/YYYY if month <= 12\n let mm = parseInt(parts[0], 10);\n let dd = parseInt(parts[1], 10);\n let yy = parseInt(parts[2], 10);\n if (!Number.isNaN(mm) && !Number.isNaN(dd) && !Number.isNaN(yy)) {\n // Normalize year\n if (yy < 100) yy += 2000;\n const d = new Date(yy, mm - 1, dd);\n if (!Number.isNaN(d.getTime())) {\n return formatDateWithOrdinal(d, locale);\n }\n }\n }\n return dateString;\n }\n const d = new Date(parsed);\n return formatDateWithOrdinal(d, locale);\n}\n\nfunction ordinalSuffix(n) {\n const s = [\"th\", \"st\", \"nd\", \"rd\"], v = n % 100;\n return (s[(v - 20) % 10] || s[v] || s[0]);\n}\n\nfunction formatDateWithOrdinal(dateObj, locale) {\n try {\n const fmt = new Intl.DateTimeFormat(locale, { month: 'long', day: 'numeric', year: 'numeric' });\n // Use formatToParts to insert ordinal for day\n if (typeof Intl.DateTimeFormat.prototype.formatToParts === 'function') {\n const parts = new Intl.DateTimeFormat(locale, { month: 'long', day: 'numeric', year: 'numeric' }).formatToParts(dateObj);\n let month = '';\n let day = '';\n let year = '';\n parts.forEach(p => {\n if (p.type === 'month') month = p.value;\n if (p.type === 'day') day = p.value;\n if (p.type === 'year') year = p.value;\n });\n const dayNum = parseInt(day, 10) || dateObj.getDate();\n return `${month} ${dayNum}${ordinalSuffix(dayNum)}, ${year}`;\n }\n return fmt.format(dateObj);\n } catch (e) {\n return dateObj.toDateString();\n }\n}\n","/**\n * Timeline color theming\n *\n * Applies dynamic color customization to timeline elements using CSS custom properties.\n * Automatically calculates accessible navigation arrow colors based on background brightness.\n */\n\nimport { getContrastColor, getColorBrightness } from '../shared/utils.js';\n\n/**\n * Apply color customization to a timeline\n *\n * Sets CSS custom properties for node, line, and navigation colors. Automatically\n * calculates arrow color (light/dark) for accessible navigation based on navColor brightness.\n *\n * @param {HTMLElement} container - Timeline container element\n * @param {Object} config - Color configuration\n * @param {string} [config.nodeColor] - CSS color for timeline nodes (circle or box)\n * @param {string} [config.lineColor] - CSS color for timeline connector line\n * @param {string} [config.navColor] - CSS color for navigation buttons/arrows\n * @returns {void}\n */\nexport function applyTimelineColors(container, config) {\n let nodeColor = config.nodeColor || null;\n let lineColor = config.lineColor || null;\n const navColor = config.navColor || null;\n\n if (nodeColor && !lineColor) lineColor = nodeColor;\n if (lineColor && !nodeColor) nodeColor = lineColor;\n\n if (nodeColor) {\n container.style.setProperty('--timeline-node-color', nodeColor);\n // Use configured nodeColor for the active/current item outline\n container.style.setProperty('--timeline-active-outline-color', nodeColor);\n }\n if (lineColor) {\n container.style.setProperty('--timeline-line-color', lineColor);\n }\n if (navColor) {\n container.style.setProperty('--timeline-nav-color', navColor);\n container.style.setProperty('--timeline-nav-border', getContrastColor(navColor));\n const brightness = getColorBrightness(navColor);\n const arrowColor = brightness > 128 ? '#333' : '#fff';\n container.style.setProperty('--timeline-arrow-color', arrowColor);\n container.setAttribute('data-arrow-color', arrowColor);\n }\n}\n","/**\n * Swiper carousel adapter for timeline\n *\n * Optional integration with Swiper (https://swiperjs.com/) for touch-friendly carousel navigation.\n * Dynamically loads Swiper library with fallback strategies and gracefully degrades if unavailable.\n *\n * Swiper resolution order:\n * 1. ESM CDN URL provided via options.swiperCdn\n * 2. NPM-installed package (dynamic import)\n * 3. Global window.Swiper (UMD CDN bundle)\n *\n * If Swiper not found, timeline still functions normally without carousel.\n */\n\nexport default class SwiperAdapter {\n /**\n * Create adapter instance\n */\n constructor() {\n /** @type {Object|null} Swiper library instance */\n this.swiper = null;\n /** @type {HTMLElement|null} Timeline wrap element (Swiper container) */\n this._container = null;\n /** @type {Object} Original DOM state (classes, attributes) for restoration on destroy */\n this._original = {};\n }\n\n /**\n * Initialize Swiper for timeline\n *\n * Attempts to resolve Swiper library and configure it for timeline carousel mode.\n * Adds required Swiper classes (swiper, swiper-wrapper, swiper-slide) to DOM.\n * Gracefully returns null if Swiper unavailable.\n *\n * @param {HTMLElement} timelineEl - Timeline container element\n * @param {Object} timelineApi - Timeline API object (unused, for future extensibility)\n * @param {Object} [options={}] - Swiper configuration options\n * @param {string} [options.swiperCdn] - ESM CDN URL for Swiper library\n * @param {...any} [options.otherOptions] - Additional Swiper options (passed to Swiper constructor)\n * @returns {Promise<Object|null>} Swiper instance, or null if initialization failed or library unavailable\n */\n async init(timelineEl, timelineApi, options = {}) {\n this._container = timelineEl.querySelector('.timeline__wrap');\n if (!this._container) {\n console.warn('SwiperAdapter: No .timeline__wrap found');\n return null;\n }\n\n let SwiperLib = null;\n // Try ESM CDN if provided via options.swiperCdn\n if (options && options.swiperCdn && typeof options.swiperCdn === 'string') {\n try {\n const mod = await import(/* @vite-ignore */ options.swiperCdn);\n SwiperLib = mod.default || mod.Swiper || mod || null;\n } catch (e) {\n console.warn('SwiperAdapter: failed to import swiper from swiperCdn:', e);\n }\n }\n\n // Try package import (npm-installed) if not already resolved\n if (!SwiperLib) {\n try {\n const mod = await import('swiper');\n SwiperLib = mod.default || mod.Swiper || mod || null;\n } catch (e) {\n // ignore - handled by fallback\n }\n }\n\n // Fallback to global window.Swiper (UMD bundle via CDN)\n if (!SwiperLib && typeof window !== 'undefined' && window.Swiper) {\n SwiperLib = window.Swiper;\n }\n\n if (!SwiperLib) {\n // Friendly, one-time notice. Remember via localStorage.\n try {\n const seen = typeof Storage !== 'undefined' && localStorage.getItem('swiperJSLib');\n if (!(seen === '0' || seen === 'false')) {\n console.log('SwiperAdapter: Swiper library not found (tried options.swiperCdn, dynamic import, and window.Swiper)');\n if (typeof Storage !== 'undefined') localStorage.setItem('swiperJSLib', '0');\n }\n } catch (e) {\n // no-op if storage inaccessible\n console.log('SwiperAdapter: Swiper library not found (tried options.swiperCdn, dynamic import, and window.Swiper)');\n }\n return null;\n }\n\n // Preserve original classes/styles so we can restore on destroy\n const items = this._container.querySelector('.timeline__items');\n this._original.itemsClass = items ? items.className : null;\n this._original.itemClasses = [];\n if (items) {\n Array.from(items.children).forEach((child) => {\n this._original.itemClasses.push(child.className || '');\n });\n }\n\n // Add Swiper required classes\n this._container.classList.add('swiper');\n if (items) {\n items.classList.add('swiper-wrapper');\n Array.from(items.children).forEach((child) => {\n child.classList.add('swiper-slide');\n });\n }\n\n // Merge sensible defaults for timeline usage\n const defaultOpts = Object.assign({\n slidesPerView: 'auto',\n freeMode: false,\n spaceBetween: 20,\n // Ensure horizontal direction\n direction: 'horizontal'\n }, options || {});\n\n try {\n if (typeof SwiperLib !== 'function') {\n console.warn('SwiperAdapter: Swiper resolved but is not a constructor');\n this.swiper = null;\n } else {\n this.swiper = new SwiperLib(this._container, defaultOpts);\n try {\n if (typeof Storage !== 'undefined') localStorage.setItem('swiperJSLib', '1');\n } catch (_) { /* ignore storage errors */ }\n }\n } catch (e) {\n console.warn('SwiperAdapter: failed to initialize Swiper instance', e);\n this.swiper = null;\n }\n\n return this.swiper;\n }\n\n slideTo(index, opts) {\n if (this.swiper && typeof this.swiper.slideTo === 'function') {\n this.swiper.slideTo(index, opts && opts.speed);\n }\n }\n\n slideBy(delta, opts) {\n if (this.swiper && typeof this.swiper.slideTo === 'function' && typeof this.swiper.activeIndex === 'number') {\n this.slideTo(this.swiper.activeIndex + delta, opts);\n }\n }\n\n update() {\n if (this.swiper && typeof this.swiper.update === 'function') this.swiper.update();\n }\n\n destroy() {\n if (this.swiper && typeof this.swiper.destroy === 'function') {\n try { this.swiper.destroy(true, true); } catch (e) { /* ignore */ }\n this.swiper = null;\n }\n\n if (this._container) {\n const items = this._container.querySelector('.timeline__items');\n this._container.classList.remove('swiper');\n if (items) {\n items.classList.remove('swiper-wrapper');\n Array.from(items.children).forEach((child, i) => {\n child.classList.remove('swiper-slide');\n if (this._original.itemClasses && this._original.itemClasses[i] !== undefined) {\n child.className = this._original.itemClasses[i];\n }\n });\n if (this._original.itemsClass !== null) items.className = this._original.itemsClass;\n }\n }\n }\n}\n","/**\n * Keyboard navigation feature for timelines\n *\n * Responsibilities:\n * - Assign sequential tabindex to nav buttons and items\n * - Support ENTER/SPACE to activate nav buttons\n * - Provide Shift+ArrowLeft/Right hotkeys to focus nav buttons\n * - Support Up/Down (vertical) and Left/Right (horizontal) item navigation\n * - Announce active node changes via aria-live for screen readers\n * - Cleanly rebind on re-initialization\n */\n\nfunction createAriaLiveRegion(timelineEl) {\n if (!timelineEl) return;\n // Check if already exists\n let region = timelineEl.querySelector('.timeline__live-region');\n if (!region) {\n region = document.createElement('div');\n region.className = 'timeline__live-region sr-only';\n region.setAttribute('aria-live', 'polite');\n region.setAttribute('aria-atomic', 'true');\n timelineEl.appendChild(region);\n }\n return region;\n}\n\nfunction announceActiveNode(timelineEl, itemEl) {\n if (!timelineEl || !itemEl) return;\n const region = timelineEl.querySelector('.timeline__live-region');\n if (!region) return;\n \n // Extract announcement text from aria-labelledby or construct from content\n const labelId = itemEl.getAttribute('aria-labelledby');\n let announcement = '';\n if (labelId) {\n const labelEl = document.getElementById(labelId);\n announcement = labelEl ? labelEl.textContent : '';\n }\n if (!announcement) {\n const date = itemEl.querySelector('.timeline__date');\n const heading = itemEl.querySelector('.timeline__heading');\n announcement = (date ? date.textContent : '') + '. ' + (heading ? heading.textContent : '');\n }\n \n // Update region text (screen readers will announce)\n region.textContent = announcement;\n}\n\nfunction setSequentialTabOrder(timelineEl) {\n if (!timelineEl) return;\n\n const prev = timelineEl.querySelector('.timeline-nav-button--prev');\n const next = timelineEl.querySelector('.timeline-nav-button--next');\n const items = Array.from(timelineEl.querySelectorAll('.timeline__item'));\n\n let tab = 1;\n if (prev) prev.tabIndex = tab++;\n items.forEach((item) => {\n // Ensure items are focusable and assign order\n if (!item.hasAttribute('tabindex')) item.setAttribute('tabindex', '0');\n item.tabIndex = tab++;\n });\n if (next) next.tabIndex = tab++;\n}\n\nfunction bindHotkeys(timelineEl, api) {\n if (!timelineEl) return;\n const prev = timelineEl.querySelector('.timeline-nav-button--prev');\n const next = timelineEl.querySelector('.timeline-nav-button--next');\n\n // Remove existing handlers if re-initialized\n const existing = timelineEl.__keyboardHandlers;\n if (existing) {\n try { timelineEl.removeEventListener('keydown', existing.keydown); } catch (_) {}\n if (prev && existing.prevKey) try { prev.removeEventListener('keydown', existing.prevKey); } catch (_) {}\n if (next && existing.nextKey) try { next.removeEventListener('keydown', existing.nextKey); } catch (_) {}\n }\n\n const keydownHandler = function(e) {\n // If Enter/Space pressed while a nav button is focused, trigger its click\n const active = document.activeElement;\n if ((e.key === 'Enter' || e.key === ' ') && (active === prev || active === next)) {\n e.preventDefault();\n try { active.click(); } catch (_) { /* ignore */ }\n return;\n }\n\n // Shift+Arrow hotkeys to focus nav buttons\n if (!e.shiftKey) return;\n if (e.key === 'ArrowLeft' && prev) {\n e.preventDefault();\n prev.focus();\n } else if (e.key === 'ArrowRight' && next) {\n e.preventDefault();\n next.focus();\n }\n };\n\n const prevKeyHandler = function(e) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n // Trigger click handler\n this.click();\n }\n };\n\n const nextKeyHandler = function(e) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this.click();\n }\n };\n\n const navClickHandler = function() {\n // Announce active node after navigation\n setTimeout(() => {\n const active = timelineEl.querySelector('.timeline__item--active');\n if (active) announceActiveNode(timelineEl, active);\n }, 0);\n };\n\n timelineEl.addEventListener('keydown', keydownHandler);\n // Also listen at document level so global hotkeys (e.g., Shift+Arrow)\n // work even if focus is not inside the timeline element (useful for tests).\n const globalKeyHandler = function(e) {\n // Ignore if focused in form controls\n const tag = (e.target && e.target.tagName) ? e.target.tagName.toLowerCase() : '';\n if (tag === 'input' || tag === 'textarea' || e.target.isContentEditable) return;\n if (!e.shiftKey) return;\n if (e.key === 'ArrowLeft' && prev) {\n e.preventDefault();\n prev.focus();\n } else if (e.key === 'ArrowRight' && next) {\n e.preventDefault();\n next.focus();\n }\n };\n document.addEventListener('keydown', globalKeyHandler);\n if (prev) prev.addEventListener('keydown', prevKeyHandler);\n if (next) next.addEventListener('keydown', nextKeyHandler);\n if (prev) prev.addEventListener('click', navClickHandler);\n if (next) next.addEventListener('click', navClickHandler);\n\n timelineEl.__keyboardHandlers = {\n keydown: keydownHandler,\n prevKey: prevKeyHandler,\n nextKey: nextKeyHandler,\n globalKey: globalKeyHandler,\n prev,\n next,\n navClick: navClickHandler\n };\n\n // Arrow navigation while focused on a node (horizontal or vertical mode)\n const items = Array.from(timelineEl.querySelectorAll('.timeline__item'));\n const itemKeyHandler = function(e) {\n const horizontal = timelineEl.classList.contains('timeline--horizontal');\n const validKeys = horizontal \n ? ['ArrowLeft', 'ArrowRight', 'Home', 'End']\n : ['ArrowUp', 'ArrowDown', 'Home', 'End'];\n if (!validKeys.includes(e.key)) return;\n e.preventDefault();\n let delta = 0;\n if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') delta = -1;\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') delta = 1;\n const all = items;\n const currentIndex = all.indexOf(e.currentTarget);\n let targetIndex = currentIndex;\n if (e.key === 'Home') targetIndex = 0;\n else if (e.key === 'End') targetIndex = all.length - 1;\n else targetIndex = Math.max(0, Math.min(all.length - 1, currentIndex + delta));\n\n const target = all[targetIndex];\n if (api && typeof api.setCurrentIndex === 'function' && typeof api.updatePosition === 'function') {\n api.setCurrentIndex(targetIndex);\n api.updatePosition();\n }\n if (target) {\n target.focus();\n announceActiveNode(timelineEl, target);\n }\n };\n\n items.forEach((item) => {\n item.addEventListener('keydown', itemKeyHandler);\n });\n timelineEl.__keyboardHandlers.itemKey = itemKeyHandler;\n timelineEl.__keyboardHandlers.items = items;\n}\n\nfunction initializeKeyboardForTimeline(timelineEl, api) {\n setSequentialTabOrder(timelineEl);\n bindHotkeys(timelineEl, api);\n createAriaLiveRegion(timelineEl);\n}\n\n// Listen for timeline initialization events and apply keyboard behavior\ndocument.addEventListener('timeline:initialized', function(ev) {\n try {\n // Prefer the target element of the event; fallback to detail.id\n const tlEl = ev.target && ev.target.classList && ev.target.classList.contains('timeline')\n ? ev.target\n : (ev.detail && ev.detail.id ? document.getElementById(ev.detail.id) : null);\n const api = ev.detail && ev.detail.api ? ev.detail.api : undefined;\n if (tlEl) initializeKeyboardForTimeline(tlEl, api);\n } catch (_) { /* noop */ }\n});\n\nfunction destroyKeyboardForTimeline(timelineEl) {\n if (!timelineEl || !timelineEl.__keyboardHandlers) return;\n const handlers = timelineEl.__keyboardHandlers;\n\n try { timelineEl.removeEventListener('keydown', handlers.keydown); } catch (_) {}\n if (handlers.prev) {\n try { handlers.prev.removeEventListener('keydown', handlers.prevKey); } catch (_) {}\n try { handlers.prev.removeEventListener('click', handlers.navClick); } catch (_) {}\n }\n if (handlers.next) {\n try { handlers.next.removeEventListener('keydown', handlers.nextKey); } catch (_) {}\n try { handlers.next.removeEventListener('click', handlers.navClick); } catch (_) {}\n }\n if (handlers.globalKey) {\n try { document.removeEventListener('keydown', handlers.globalKey); } catch (_) {}\n }\n if (handlers.items && handlers.itemKey) {\n handlers.items.forEach((item) => {\n try { item.removeEventListener('keydown', handlers.itemKey); } catch (_) {}\n });\n }\n\n delete timelineEl.__keyboardHandlers;\n}\n\nexport { initializeKeyboardForTimeline, setSequentialTabOrder, destroyKeyboardForTimeline };\n","/**\n * layout-fallbacks.js\n * \n * Applies CSS class fallbacks for missing images and summaries in timeline nodes.\n * These fallbacks ensure proper visual layout when optional content is absent.\n * \n * - `.no-image` is added to `.timeline__content` when the image is missing or fails to load\n * - `.no-summary` is added to `.timeline__content` when the summary is empty or missing\n * \n * The module also observes timeline containers for dynamically inserted items and\n * automatically applies fallbacks to new content.\n */\n\n/**\n * Apply .no-image class to timeline content nodes when images are absent or fail to load\n * @param {Document|HTMLElement} root - Root element to search within (defaults to document)\n */\nfunction applyNoImageFallback(root = document) {\n const contents = (root || document).querySelectorAll('.timeline__content');\n contents.forEach(c => {\n const img = c.querySelector('.timeline__image');\n if (!img) {\n c.classList.add('no-image');\n return;\n }\n\n // If image exists, check load state\n if (img.complete) {\n if (img.naturalWidth === 0) c.classList.add('no-image'); else c.classList.remove('no-image');\n } else {\n img.addEventListener('load', () => c.classList.remove('no-image'), { once: true });\n img.addEventListener('error', () => c.classList.add('no-image'), { once: true });\n }\n });\n}\n\n/**\n * Apply .no-summary class to timeline content nodes when summary is absent or empty\n * @param {Document|HTMLElement} root - Root element to search within (defaults to document)\n */\nfunction applyNoSummaryFallback(root = document) {\n const contents = (root || document).querySelectorAll('.timeline__content');\n contents.forEach(c => {\n const summary = c.querySelector('.timeline__summary');\n const hasSummary = !!(summary && summary.textContent && summary.textContent.trim().length > 0);\n if (!hasSummary) {\n c.classList.add('no-summary');\n } else {\n c.classList.remove('no-summary');\n }\n });\n}\n\n/**\n * Observe timeline item containers for dynamic insertions and apply fallbacks to new content\n * Uses MutationObserver to watch for DOM changes in .timeline__items containers\n */\nfunction observeTimelineInsertions() {\n const containers = document.querySelectorAll('.timeline__items');\n containers.forEach(container => {\n const mo = new MutationObserver(() => { \n applyNoImageFallback(container); \n applyNoSummaryFallback(container); \n });\n mo.observe(container, { childList: true, subtree: true });\n });\n}\n\n/**\n * Initialize layout fallbacks for all timeline content\n * Call this after timeline is loaded or when content is added\n * @param {Document|HTMLElement} root - Root element to search within (defaults to document)\n */\nexport function initLayoutFallbacks(root = document) {\n applyNoImageFallback(root);\n applyNoSummaryFallback(root);\n observeTimelineInsertions();\n}\n\nexport {\n applyNoImageFallback,\n applyNoSummaryFallback,\n observeTimelineInsertions\n};\n","import { showTimelineError } from '../features/error-ui.js';\nimport { applyTimelineColors } from '../features/colors.js';\nimport { openTimelineModal } from '../features/modals.js';\nimport { timelineRegistry } from '../shared/state.js';\nimport SwiperAdapter from '../../adapters/swiper-adapter.js';\nimport { formatAccessibleDate } from '../shared/utils.js';\nimport { handleDeepLinking } from '../features/deep-linking.js';\nimport { destroyKeyboardForTimeline } from '../features/keyboard.js';\nimport { initLayoutFallbacks } from '../features/layout-fallbacks.js';\n\n/**\n * Calculate and apply responsive scaling for horizontal timeline based on viewport height\n *\n * Automatically adjusts node dimensions, image sizes, and font sizes to fit the available\n * viewport height while respecting minimum and maximum constraints. Uses CSS custom properties\n * to apply scaling dynamically without rebuilding the DOM.\n *\n * @param {HTMLElement} timelineEl - The timeline container element\n * @returns {void}\n */\nfunction calculateHorizontalScale(timelineEl) {\n if (!timelineEl || !timelineEl.classList.contains('timeline--horizontal')) {\n return;\n }\n\n // Define constraints\n const constraints = {\n nodeWidth: { min: 150, max: 200, default: 200 },\n nodeMinHeight: { min: 135, max: 180, default: 180 },\n imageSize: { min: 80, max: 100, default: 100 },\n titleFontSize: { min: 14, max: 18, default: 18 },\n textFontSize: { min: 11, max: 13, default: 11 }\n };\n\n // Get available viewport height (minus padding/margins)\n const viewportHeight = window.innerHeight;\n const timelinePadding = 180; // Account for heading, margins, bottom item padding (40px), and buffer\n const availableHeight = viewportHeight - timelinePadding;\n\n // Calculate required height for default (max) dimensions\n // Two rows of nodes + divider line + spacing + bottom padding\n const maxNodeHeight = constraints.nodeMinHeight.max;\n const requiredHeight = (maxNodeHeight * 2) + 90; // 90px for divider, spacing, and bottom item padding\n\n // Calculate scale factor\n let scaleFactor = 1.0;\n if (availableHeight < requiredHeight) {\n scaleFactor = Math.max(0.75, availableHeight / requiredHeight); // Don't scale below 75%\n }\n\n // Apply scaling with constraints\n function scaleValue(config) {\n const scaled = Math.round(config.default * scaleFactor);\n return Math.max(config.min, Math.min(config.max, scaled));\n }\n\n // Set CSS custom properties\n timelineEl.style.setProperty('--timeline-h-node-width', scaleValue(constraints.nodeWidth) + 'px');\n timelineEl.style.setProperty('--timeline-h-node-min-height', scaleValue(constraints.nodeMinHeight) + 'px');\n timelineEl.style.setProperty('--timeline-h-image-size', scaleValue(constraints.imageSize) + 'px');\n timelineEl.style.setProperty('--timeline-h-title-font-size', scaleValue(constraints.titleFontSize) + 'px');\n timelineEl.style.setProperty('--timeline-h-text-font-size', scaleValue(constraints.textFontSize) + 'px');\n}\n\nfunction ensureInlineModalData(itemEl) {\n if (!itemEl) return;\n const content = itemEl.querySelector('.timeline__content') || itemEl;\n if (!itemEl.hasAttribute('data-modal-title')) {\n const heading = content.querySelector('h1,h2,h3,h4,h5,h6');\n if (heading && heading.textContent) {\n itemEl.setAttribute('data-modal-title', heading.textContent.trim());\n }\n }\n if (!itemEl.hasAttribute('data-modal-content')) {\n const firstP = content.querySelector('p');\n if (firstP && firstP.textContent) {\n itemEl.setAttribute('data-modal-content', firstP.textContent.trim());\n }\n }\n if (!itemEl.hasAttribute('data-modal-image')) {\n const img = content.querySelector('img');\n if (img && img.getAttribute('src')) {\n itemEl.setAttribute('data-modal-image', img.getAttribute('src'));\n }\n }\n}\n\nfunction ensureAccessibleLabelForInlineItem(itemEl) {\n if (!itemEl) return;\n // If an explicit aria-label is present, leave it alone\n if (itemEl.hasAttribute('aria-label')) return;\n // If already aria-labelledby, nothing to do\n if (itemEl.hasAttribute('aria-labelledby')) return;\n\n // Check for data-aria-label attribute (dataset.ariaLabel)\n const dataLabel = itemEl.dataset && itemEl.dataset.ariaLabel;\n let labelText = dataLabel && String(dataLabel).trim();\n\n // Otherwise, build from existing DOM: date + heading\n if (!labelText || labelText === '') {\n const dateEl = itemEl.querySelector('.timeline__date');\n const headingEl = itemEl.querySelector('.timeline__heading');\n const dateText = dateEl ? dateEl.textContent.trim() : '';\n const headingText = headingEl ? headingEl.textContent.trim() : '';\n if (dateText || headingText) {\n const formattedDate = dateText ? formatAccessibleDate(dateText) : '';\n labelText = (dateText ? `Date: ${formattedDate}. ` : '') + (headingText ? `Title: ${headingText}` : '');\n const idAttr = itemEl.getAttribute('data-node-id');\n if (idAttr) labelText = `Node ${idAttr}: ` + labelText;\n }\n }\n\n if (labelText && labelText !== '') {\n const labelId = 'tl-label-' + Math.random().toString(36).slice(2, 9);\n const sr = document.createElement('span');\n sr.className = 'sr-only';\n sr.id = labelId;\n sr.textContent = labelText;\n itemEl.appendChild(sr);\n itemEl.setAttribute('aria-labelledby', labelId);\n }\n}\n\nfunction enhanceInlineItems(timelineEl, items) {\n if (!items || !items.length) return;\n items.forEach(function(item){\n if (item.getAttribute('data-modal-bound') === '1') return;\n ensureInlineModalData(item);\n ensureAccessibleLabelForInlineItem(item);\n const hasModal = item.hasAttribute('data-modal-title') || item.hasAttribute('data-modal-content') || item.hasAttribute('data-modal-image') || item.hasAttribute('data-modal-html');\n if (hasModal) {\n item.addEventListener('click', function(e){\n e.preventDefault();\n openTimelineModal(item);\n });\n item.setAttribute('data-modal-bound', '1');\n }\n });\n}\n\nfunction createArrowSVG(direction, color) {\n if (direction === 'left') {\n return '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"7.8\" height=\"14\" style=\"display:block;margin:auto;\"><path fill=\"none\" stroke=\"' + color + '\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6.8 1L1 7l5.8 6\"/></svg>';\n }\n return '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"7.8\" height=\"14\" style=\"display:block;margin:auto;\"><path fill=\"none\" stroke=\"' + color + '\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M1 1l5.8 6L1 13\"/></svg>';\n}\n\nfunction clampInt(value, min, max) {\n const n = parseInt(value, 10);\n if (Number.isNaN(n)) return min;\n return Math.max(min, Math.min(max, n));\n}\n\n// Exported resolver for testing and external use\n/**\n * Resolve the effective node side for sameSideNodes feature\n *\n * This is the same logic used internally by the timeline engine but\n * exposed as a top-level export to allow unit testing.\n *\n * @param {Object} settings - Timeline settings object\n * @param {string} mode - 'horizontal' or 'vertical'\n * @param {boolean} rtl - Right-to-left mode flag\n * @returns {string|null}\n */\nexport function resolveSide(settings, mode, rtl) {\n const hDefault = 'top';\n const vDefault = 'left';\n\n let s = settings && settings.sameSideNodes;\n if (s === undefined || s === false || s === 'false') return null;\n\n if (s === 'true' || s === true) {\n if (mode === 'horizontal') return settings.horizontalStartPosition || hDefault;\n return settings.verticalStartPosition || vDefault;\n }\n\n s = String(s).toLowerCase();\n if (mode === 'horizontal') {\n if (s === 'top' || s === 'bottom') return s;\n if (s === 'left') return 'top';\n if (s === 'right') return 'bottom';\n return hDefault;\n }\n\n if (settings && settings.verticalStartPosition) return settings.verticalStartPosition;\n if (s === 'top') {\n return rtl ? 'right' : 'left';\n }\n if (s === 'bottom') {\n return rtl ? 'left' : 'right';\n }\n if (s === 'left' || s === 'right') return s;\n return vDefault;\n}\n\n/**\n * Check if URL deep-link should update this specific timeline instance\n * @param {HTMLElement} timelineEl - Timeline container element\n * @returns {boolean} - True if this timeline should respond to URL deep-link\n * @private\n */\nfunction shouldUpdateDeepLinkForTimeline(timelineEl) {\n if (typeof window === 'undefined' || !window.location) return false;\n const params = new URLSearchParams(window.location.search);\n if (!params.has('id')) return false;\n const timelineParam = params.get('timeline');\n // If a specific timeline is deep-linked, only update URL for that one.\n if (timelineParam && timelineEl && timelineEl.id) {\n return timelineParam === timelineEl.id;\n }\n // If no timeline param provided, treat as generic deep link.\n return true;\n}\n\n/**\n * Update browser URL with current timeline item ID for deep linking\n * @param {HTMLElement} timelineEl - Timeline container element\n * @param {string|number} nodeId - ID of the current node\n * @returns {void}\n * @private\n */\nfunction updateDeepLinkUrl(timelineEl, nodeId) {\n if (!timelineEl || !nodeId || typeof window === 'undefined' || !window.history) return;\n if (!shouldUpdateDeepLinkForTimeline(timelineEl)) return;\n const url = new URL(window.location.href);\n const params = url.searchParams;\n if (timelineEl.id) {\n params.set('timeline', timelineEl.id);\n }\n params.set('id', String(nodeId));\n url.search = params.toString();\n window.history.replaceState({}, '', url.toString());\n}\n\nexport function timeline(collection, options) {\n /**\n * Initialize and render timeline(s) for the provided element(s)\n *\n * Main API entry point. Accepts a DOM element or collection and initializes timeline(s)\n * with responsive mode switching, IntersectionObserver animation, event handling, and cleanup.\n *\n * @param {HTMLElement|NodeList|HTMLCollection} collection - Target element(s) to initialize timeline\n * @param {Object} [options] - Configuration options (overrides data attributes)\n * @param {string} [options.mode='vertical'] - 'horizontal' or 'vertical' layout\n * @param {number} [options.minWidth=600] - Width below which to switch to vertical (for horizontal)\n * @param {number} [options.maxWidth=600] - Width above which to switch to horizontal (for vertical)\n * @param {string} [options.horizontalStartPosition='top'] - 'top' or 'bottom' for horizontal\n * @param {string} [options.verticalStartPosition='left'] - 'left' or 'right' for vertical\n * @param {number} [options.startIndex=0] - Initial index to display\n * @param {number} [options.moveItems=1] - Number of items to scroll with nav buttons\n * @param {boolean} [options.rtlMode=false] - Right-to-left layout support\n * @param {string} [options.verticalTrigger='15%'] - When to show items in vertical (px or %)\n * @param {string} [options.useSwiper='false'] - 'true'|'false'|'auto' for Swiper carousel\n * @param {string} [options.sameSideNodes='false'] - Render all nodes on same side (feature)\n * @param {Object} [options.nodeColor] - CSS color for timeline nodes\n * @param {Object} [options.lineColor] - CSS color for timeline line\n * @param {Object} [options.navColor] - CSS color for navigation elements\n * @returns {void}\n */\n const timelines = [];\n const warningLabel = 'Timeline:';\n let winWidth = window.innerWidth;\n let resizeTimer;\n const eventListeners = new Map(); // Track event listeners for cleanup on destroy/reset\n\n const defaultSettings = {\n minWidth: { type: 'integer', defaultValue: 600 },\n maxWidth: { type: 'integer', defaultValue: 600 },\n horizontalStartPosition: { type: 'string', acceptedValues: ['bottom', 'top'], defaultValue: 'top' },\n mode: { type: 'string', acceptedValues: ['horizontal', 'vertical'], defaultValue: 'vertical' },\n moveItems: { type: 'integer', defaultValue: 1 },\n rtlMode: { type: 'boolean', acceptedValues: [true, false], defaultValue: false },\n startIndex: { type: 'integer', defaultValue: 0 },\n verticalStartPosition: { type: 'string', acceptedValues: ['left', 'right'], defaultValue: 'left' },\n verticalTrigger: { type: 'string', defaultValue: '15%' },\n useSwiper: { type: 'string', acceptedValues: ['false', 'true', 'auto'], defaultValue: 'false' },\n sameSideNodes: { type: 'string', acceptedValues: ['top', 'bottom', 'left', 'right', 'true', 'false'], defaultValue: 'false' }\n };\n\n // Helper to resolve effective side based on sameSideNodes setting and orientation\n /**\n * Resolve the effective node side for sameSideNodes feature\n *\n * When sameSideNodes is enabled, determines which side (top/bottom/left/right) all timeline\n * nodes should render on, based on configuration and orientation. Handles RTL mode and\n * responsive orientation switching.\n *\n * @param {Object} settings - Timeline settings object\n * @param {string} mode - 'horizontal' or 'vertical'\n * @param {boolean} rtl - Right-to-left mode flag\n * @returns {string|null} - 'top'|'bottom'|'left'|'right' or null if sameSideNodes disabled\n * @private\n */\n function resolveSide(settings, mode, rtl) {\n // mode: 'horizontal' or 'vertical'\n const hDefault = 'top';\n const vDefault = 'left';\n\n let s = settings.sameSideNodes;\n if (s === undefined || s === false || s === 'false') return null;\n\n // Normalize string values\n if (s === 'true' || s === true) {\n // Boolean true: use orientation-specific start position (explicit or default)\n if (mode === 'horizontal') return settings.horizontalStartPosition || hDefault;\n return settings.verticalStartPosition || vDefault;\n }\n\n // s is an explicit string: could be 'top'|'bottom'|'left'|'right'\n s = String(s).toLowerCase();\n if (mode === 'horizontal') {\n // If explicit horizontal string provided, prefer it\n if (s === 'top' || s === 'bottom') return s;\n // If explicit vertical string provided, map to horizontal (left->top, right->bottom)\n if (s === 'left') return 'top';\n if (s === 'right') return 'bottom';\n return hDefault;\n }\n\n // vertical mode: prefer explicit verticalStartPosition if provided in settings\n if (settings.verticalStartPosition) return settings.verticalStartPosition;\n // Map explicit horizontal string to vertical side\n if (s === 'top') {\n return rtl ? 'right' : 'left';\n }\n if (s === 'bottom') {\n return rtl ? 'left' : 'right';\n }\n if (s === 'left' || s === 'right') return s;\n return vDefault;\n }\n\n function testValues(value, settingName) {\n if (typeof value !== 'number' && value % 1 !== 0) {\n console.warn(`${warningLabel} The value \"${value}\" entered for the setting \"${settingName}\" is not an integer.`);\n return false;\n }\n return true;\n }\n\n function itemWrap(el, wrapper, classes) {\n wrapper.classList.add(classes);\n el.parentNode.insertBefore(wrapper, el);\n wrapper.appendChild(el);\n }\n\n function wrapElements(items) {\n items.forEach((item) => {\n itemWrap(item.querySelector('.timeline__content'), document.createElement('div'), 'timeline__content__wrap');\n itemWrap(item.querySelector('.timeline__content__wrap'), document.createElement('div'), 'timeline__item__inner');\n });\n }\n\n function isElementInViewport(el, triggerPosition) {\n const rect = el.getBoundingClientRect();\n const windowHeight = window.innerHeight || document.documentElement.clientHeight;\n const defaultTrigger = defaultSettings.verticalTrigger.defaultValue.match(/(\\d*\\.?\\d*)(.*)/);\n let triggerUnit = triggerPosition.unit;\n let triggerValue = triggerPosition.value;\n let trigger = windowHeight;\n if (triggerUnit === 'px' && triggerValue >= windowHeight) {\n console.warn('The value entered for the setting \"verticalTrigger\" is larger than the window height. The default value will be used instead.');\n [, triggerValue, triggerUnit] = defaultTrigger;\n }\n if (triggerUnit === 'px') {\n trigger = parseInt(trigger - triggerValue, 10);\n } else if (triggerUnit === '%') {\n trigger = parseInt(trigger * ((100 - triggerValue) / 100), 10);\n }\n return (\n rect.top <= trigger &&\n rect.left <= (window.innerWidth || document.documentElement.clientWidth) &&\n (rect.top + rect.height) >= 0 &&\n (rect.left + rect.width) >= 0\n );\n }\n\n function addTransforms(el, transform) {\n el.style.webkitTransform = transform;\n el.style.msTransform = transform;\n el.style.transform = transform;\n }\n\n function createTimelines(timelineEl) {\n // Prevent double-initialization (e.g. JSON auto-init vs manual init)\n if (timelineEl && timelineEl.getAttribute && timelineEl.getAttribute('data-timeline-initialized') === '1') {\n return;\n }\n const timelineName = timelineEl.id ? `#${timelineEl.id}` : `.${timelineEl.className}`;\n const errorPart = 'could not be found as a direct descendant of';\n const data = timelineEl.dataset;\n let wrap;\n let scroller;\n let items;\n const settings = {};\n\n try {\n wrap = timelineEl.querySelector('.timeline__wrap');\n if (!wrap) {\n throw new Error(`${warningLabel} .timeline__wrap ${errorPart} ${timelineName}`);\n } else {\n scroller = wrap.querySelector('.timeline__items');\n if (!scroller) {\n throw new Error(`${warningLabel} .timeline__items ${errorPart} .timeline__wrap`);\n } else {\n items = [].slice.call(scroller.children, 0);\n // If there are no items yet (e.g. JSON timeline not rendered yet), skip initialization.\n if (!items || items.length === 0) {\n return;\n }\n }\n }\n } catch (e) {\n console.warn(e.message);\n showTimelineError(timelineEl, 'missing-element', e.message);\n return false;\n }\n\n Object.keys(defaultSettings).forEach((key) => {\n settings[key] = defaultSettings[key].defaultValue;\n\n \n\n if (key === 'minWidth') {\n let candidate = undefined;\n if (data.minWidth !== undefined) candidate = data.minWidth;\n if (data.minwidth !== undefined) candidate = data.minwidth;\n if (data.forceVerticalMode !== undefined) candidate = data.forceVerticalMode;\n if (data.forceverticalmode !== undefined) candidate = data.forceverticalmode;\n if (candidate === undefined && options) {\n if (options.minWidth !== undefined) candidate = options.minWidth;\n else if (options.forceVerticalMode !== undefined) candidate = options.forceVerticalMode;\n }\n if (candidate !== undefined) settings.minWidth = candidate;\n } else if (key === 'maxWidth') {\n let candidate = undefined;\n if (data.maxWidth !== undefined) candidate = data.maxWidth;\n if (data.maxwidth !== undefined) candidate = data.maxwidth;\n if (candidate === undefined && options) {\n if (options.maxWidth !== undefined) candidate = options.maxWidth;\n }\n if (candidate !== undefined) settings.maxWidth = candidate;\n } else {\n if (data[key]) {\n settings[key] = data[key];\n } else if (options && options[key] !== undefined) {\n settings[key] = options[key];\n }\n }\n\n if (defaultSettings[key].type === 'integer') {\n if (!settings[key] || !testValues(settings[key], key)) {\n settings[key] = defaultSettings[key].defaultValue;\n }\n } else if (defaultSettings[key].type === 'string') {\n if (defaultSettings[key].acceptedValues && defaultSettings[key].acceptedValues.indexOf(settings[key]) === -1) {\n console.warn(`${warningLabel} The value \"${settings[key]}\" entered for the setting \"${key}\" was not recognised.`);\n settings[key] = defaultSettings[key].defaultValue;\n }\n }\n });\n\n (function applyColorParity(){\n const data = timelineEl.dataset;\n const getData = function(k){\n return data[k] !== undefined ? data[k] : (data[k && k.toLowerCase()] !== undefined ? data[k.toLowerCase()] : undefined);\n };\n let nodeColor = getData('nodeColor');\n let lineColor = getData('lineColor');\n let navColor = getData('navColor');\n if (options) {\n if (options.nodeColor !== undefined) nodeColor = options.nodeColor;\n if (options.lineColor !== undefined) lineColor = options.lineColor;\n if (options.navColor !== undefined) navColor = options.navColor;\n }\n if (nodeColor || lineColor || navColor) {\n applyTimelineColors(timelineEl, { nodeColor, lineColor, navColor });\n }\n })();\n\n const defaultTrigger = defaultSettings.verticalTrigger.defaultValue.match(/(\\d*\\.?\\d*)(.*)/);\n const triggerArray = settings.verticalTrigger.match(/(\\d*\\.?\\d*)(.*)/);\n let [, triggerValue, triggerUnit] = triggerArray;\n let triggerValid = true;\n if (!triggerValue) {\n console.warn(`${warningLabel} No numercial value entered for the 'verticalTrigger' setting.`);\n triggerValid = false;\n }\n if (triggerUnit !== 'px' && triggerUnit !== '%') {\n console.warn(`${warningLabel} The setting 'verticalTrigger' must be a percentage or pixel value.`);\n triggerValid = false;\n }\n if (triggerUnit === '%' && (triggerValue > 100 || triggerValue < 0)) {\n console.warn(`${warningLabel} The 'verticalTrigger' setting value must be between 0 and 100 if using a percentage value.`);\n triggerValid = false;\n } else if (triggerUnit === 'px' && triggerValue < 0) {\n console.warn(`${warningLabel} The 'verticalTrigger' setting value must be above 0 if using a pixel value.`);\n triggerValid = false;\n }\n\n if (triggerValid === false) {\n [, triggerValue, triggerUnit] = defaultTrigger;\n }\n\n settings.verticalTrigger = {\n unit: triggerUnit,\n value: triggerValue\n };\n\n // Sanity-check moveItems: cap to total items if it's larger than available\n if (settings.moveItems > items.length) {\n console.warn(`${warningLabel} The value of \"moveItems\" (${settings.moveItems}) is larger than the total number of items (${items.length}). It has been reduced to ${items.length}.`);\n settings.moveItems = items.length;\n }\n\n // Sanity-check startIndex: ensure it's within 0..items.length-1\n if (settings.startIndex < 0) {\n console.warn(`${warningLabel} The 'startIndex' setting must be >= 0. The value of 0 has been used instead.`);\n settings.startIndex = 0;\n } else if (settings.startIndex > Math.max(0, items.length - 1)) {\n console.warn(`${warningLabel} The 'startIndex' setting is larger than the last index for this timeline. It has been reduced to ${Math.max(0, items.length - 1)}.`);\n settings.startIndex = Math.max(0, items.length - 1);\n }\n\n // Swiper integration settings (optional)\n // Accept via data attributes (data-use-swiper=\"true|auto\") or via options.swiperAdapter / options.useSwiper\n if (data.useSwiper !== undefined || data.useswiper !== undefined) {\n const val = data.useSwiper !== undefined ? data.useSwiper : data.useswiper;\n settings.useSwiper = val === 'true' || val === 'auto' ? val : (val === 'true');\n }\n if (options) {\n if (options.useSwiper !== undefined) settings.useSwiper = options.useSwiper;\n if (options.swiperOptions !== undefined) settings.swiperOptions = options.swiperOptions;\n if (options.swiperAdapter !== undefined) settings.swiperAdapter = options.swiperAdapter;\n }\n\n enhanceInlineItems(timelineEl, items);\n\n if (!timelineEl.id) {\n timelineEl.setAttribute('data-timeline-id', 'timeline-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9));\n }\n\n // Mark as initialized to avoid duplicate initialisation attempts\n try {\n timelineEl.setAttribute('data-timeline-initialized', '1');\n } catch (e) {\n /* ignore */\n }\n\n timelines.push({\n timelineEl,\n wrap,\n scroller,\n items,\n settings,\n listeners: [], // Store listeners for cleanup\n adapter: null\n });\n }\n\n if (collection.length) {\n Array.from(collection).forEach(createTimelines);\n }\n\n function setHeightandWidths(tl) {\n function setWidths() {\n // Get the current scaled node width from CSS variable\n const computedStyle = getComputedStyle(tl.timelineEl);\n const nodeWidth = parseInt(computedStyle.getPropertyValue('--timeline-h-node-width')) || 200;\n tl.itemWidth = nodeWidth;\n tl.items.forEach((item) => {\n item.style.width = `${tl.itemWidth}px`;\n });\n tl.scrollerWidth = tl.itemWidth * tl.items.length;\n tl.scroller.style.width = `${tl.scrollerWidth}px`;\n }\n\n function setHeights() {\n let oddIndexTallest = 0;\n let evenIndexTallest = 0;\n tl.items.forEach((item, i) => {\n item.style.height = 'auto';\n const height = item.offsetHeight;\n if (i % 2 === 0) {\n evenIndexTallest = height > evenIndexTallest ? height : evenIndexTallest;\n } else {\n oddIndexTallest = height > oddIndexTallest ? height : oddIndexTallest;\n }\n });\n\n const transformString = `translateY(${evenIndexTallest}px)`;\n // Determine effective horizontal start side (top/bottom) based on sameSideNodes or explicit setting\n const effectiveHSide = resolveSide(tl.settings, 'horizontal', tl.settings.rtlMode) || tl.settings.horizontalStartPosition;\n const isSameSide = !!resolveSide(tl.settings, 'horizontal', tl.settings.rtlMode); // True if sameSideNodes is enabled\n if (isSameSide) {\n tl.timelineEl.classList.add('timeline--same-side');\n } else {\n tl.timelineEl.classList.remove('timeline--same-side');\n }\n tl.items.forEach((item, i) => {\n if (i % 2 === 0) {\n item.style.height = `${evenIndexTallest}px`;\n if (effectiveHSide === 'bottom') {\n item.classList.add('timeline__item--bottom');\n if (isSameSide) {\n // When sameSideNodes is set, don't apply transform to maintain same-side rendering\n } else {\n addTransforms(item, transformString);\n }\n } else {\n item.classList.add('timeline__item--top');\n }\n } else {\n item.style.height = `${oddIndexTallest}px`;\n if (isSameSide) {\n // When sameSideNodes is enabled, apply the same side as effectiveHSide\n if (effectiveHSide === 'bottom') {\n item.classList.add('timeline__item--bottom');\n } else {\n item.classList.add('timeline__item--top');\n }\n } else {\n // When sameSideNodes is disabled, alternate sides\n if (effectiveHSide !== 'bottom') {\n item.classList.add('timeline__item--bottom');\n addTransforms(item, transformString);\n } else {\n item.classList.add('timeline__item--top');\n }\n }\n }\n });\n // Store heights for use in divider and arrow positioning \n tl.evenIndexTallest = evenIndexTallest;\n tl.oddIndexTallest = oddIndexTallest;\n // Set scroller height\n // When same-side mode is active, use only the tallest height (not summed)\n // When alternating, use sum of top and bottom heights\n // Add 10px buffer to prevent dot clipping (dots extend 10px beyond their center)\n if (isSameSide) {\n const tallestHeight = Math.max(evenIndexTallest, oddIndexTallest);\n tl.scroller.style.height = `${tallestHeight + 10}px`;\n } else {\n tl.scroller.style.height = `${evenIndexTallest + oddIndexTallest}px`;\n }\n // Compute how many items fit in the viewport for this timeline\n try {\n tl.computedVisibleCount = Math.floor(tl.wrap.offsetWidth / tl.itemWidth) || 1;\n } catch (e) {\n tl.computedVisibleCount = 1;\n }\n }\n\n if (window.innerWidth > tl.settings.minWidth) {\n setWidths();\n setHeights();\n }\n }\n\n function addNavigation(tl) {\n const viewportWidth = tl.wrap.offsetWidth;\n const itemsVisible = Math.floor(viewportWidth / tl.itemWidth);\n\n if (tl.items.length > itemsVisible) {\n const prevArrow = document.createElement('button');\n const nextArrow = document.createElement('button');\n \n // Calculate button position based on same-side mode\n const effectiveHSide = resolveSide(tl.settings, 'horizontal', tl.settings.rtlMode) || tl.settings.horizontalStartPosition;\n const isSameSide = !!resolveSide(tl.settings, 'horizontal', tl.settings.rtlMode);\n \n let topPosition;\n if (isSameSide) {\n if (effectiveHSide === 'bottom') {\n // Bottom mode: buttons centered in top padding (30px)\n topPosition = 30;\n } else {\n // Top mode: buttons centered in bottom padding (item height - 30px)\n topPosition = tl.items[0].offsetHeight - 30;\n }\n } else {\n // Alternating mode: buttons centered on the divider\n topPosition = tl.items[0].offsetHeight;\n }\n \n prevArrow.className = 'timeline-nav-button timeline-nav-button--prev';\n nextArrow.className = 'timeline-nav-button timeline-nav-button--next';\n prevArrow.textContent = 'Previous';\n prevArrow.title = 'Go to previous items';\n nextArrow.textContent = 'Next';\n nextArrow.title = 'Go to next items';\n prevArrow.setAttribute('aria-label', 'Previous timeline items');\n nextArrow.setAttribute('aria-label', 'Next timeline items');\n prevArrow.style.top = `${topPosition}px`;\n nextArrow.style.top = `${topPosition}px`;\n\n const arrowColor = tl.timelineEl.getAttribute('data-arrow-color') || '#333';\n prevArrow.innerHTML = createArrowSVG('left', arrowColor);\n nextArrow.innerHTML = createArrowSVG('right', arrowColor);\n\n const maxActiveIndex = Math.max(0, tl.items.length - 1);\n if (tl.activeIndex <= 0) {\n prevArrow.classList.add('timeline-nav-button--at-start');\n prevArrow.title = 'Already at beginning of timeline';\n prevArrow.setAttribute('aria-disabled', 'true');\n } else {\n prevArrow.setAttribute('aria-disabled', 'false');\n }\n if (tl.activeIndex >= maxActiveIndex) {\n nextArrow.classList.add('timeline-nav-button--at-end');\n nextArrow.title = 'Already at end of timeline';\n nextArrow.setAttribute('aria-disabled', 'true');\n } else {\n nextArrow.setAttribute('aria-disabled', 'false');\n }\n tl.timelineEl.appendChild(prevArrow);\n tl.timelineEl.appendChild(nextArrow);\n }\n }\n\n function addHorizontalDivider(tl) {\n const divider = tl.timelineEl.querySelector('.timeline-divider');\n if (divider) {\n tl.timelineEl.removeChild(divider);\n }\n // Determine if same-side mode is active and which side\n const effectiveHSide = resolveSide(tl.settings, 'horizontal', tl.settings.rtlMode) || tl.settings.horizontalStartPosition;\n const isSameSide = !!resolveSide(tl.settings, 'horizontal', tl.settings.rtlMode);\n \n let topPosition;\n if (isSameSide) {\n if (effectiveHSide === 'bottom') {\n // Bottom mode: divider positioned in the 60px padding at the top of items\n // Position it in the middle of the padding (30px)\n topPosition = 30;\n } else {\n // Top mode: divider positioned in the 60px padding at the bottom of items\n // First item height + middle of padding\n topPosition = tl.items[0].offsetHeight - 30;\n }\n } else {\n // Alternating mode: divider between top and bottom nodes\n topPosition = tl.items[0].offsetHeight;\n }\n \n const horizontalDivider = document.createElement('span');\n horizontalDivider.className = 'timeline-divider';\n horizontalDivider.style.top = `${topPosition}px`;\n tl.timelineEl.appendChild(horizontalDivider);\n }\n\n function timelinePosition(tl, index) {\n const safeIndex = clampInt(index, 0, Math.max(0, tl.items.length - 1));\n const originalLeft = tl.items[safeIndex].offsetLeft;\n \n // Account for horizontal timeline padding (60px default on each side) to ensure items\n // aren't clipped on either edge. Calculate the position that keeps the item fully visible.\n const timelineStyle = window.getComputedStyle(tl.timelineEl);\n const paddingLeft = parseInt(timelineStyle.paddingLeft, 10) || 0;\n const paddingRight = parseInt(timelineStyle.paddingRight, 10) || 0;\n const viewportWidth = tl.wrap.offsetWidth;\n const itemWidth = tl.items[safeIndex].offsetWidth;\n \n // Two constraints:\n // 1. Left edge: position <= originalLeft - paddingLeft (so item appears after left padding)\n // 2. Right edge: position >= originalLeft + itemWidth - viewportWidth + paddingRight (so item ends before right padding)\n let position = originalLeft - paddingLeft;\n const minPositionForRight = Math.max(0, originalLeft + itemWidth - viewportWidth + paddingRight);\n position = Math.max(position, minPositionForRight);\n position = Math.max(0, position);\n \n const str = `translate3d(-${position}px, 0, 0)`;\n addTransforms(tl.scroller, str);\n }\n\n function updateActiveItem(tl, index) {\n // Remove active class from all items\n tl.items.forEach(item => item.classList.remove('timeline__item--active'));\n // Add active class to current item\n if (tl.items[index]) {\n tl.items[index].classList.add('timeline__item--active');\n }\n }\n\n function slideTimeline(tl) {\n const navArrows = tl.timelineEl.querySelectorAll('.timeline-nav-button');\n const arrowPrev = tl.timelineEl.querySelector('.timeline-nav-button--prev');\n const arrowNext = tl.timelineEl.querySelector('.timeline-nav-button--next');\n\n const viewportWidth = tl.wrap.offsetWidth;\n const itemsVisible = Math.floor(viewportWidth / tl.itemWidth);\n // maxScrollIndex is intentionally not used for positioning because we want to scroll to the\n // actual active item, even if that means translating beyond the last \"full\" viewport chunk.\n\n const moveItems = parseInt(tl.settings.moveItems, 10);\n \n const handleArrowClick = function(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n\n const maxActiveIndex = Math.max(0, tl.items.length - 1);\n const direction = this.classList.contains('timeline-nav-button--next') ? 1 : -1;\n \n // Calculate next active index, but when going backward and at a clamped boundary,\n // ensure we follow the \"ideal\" path by finding the nearest step-aligned index below current\n let nextActive = tl.activeIndex + (direction * moveItems);\n \n // If going backward from a potentially clamped position, find the previous aligned index\n if (direction < 0 && tl.activeIndex > 0) {\n // Calculate what the ideal indices would be starting from 0: 0, moveItems, 2*moveItems, ...\n // We need the nearest aligned index *below* the current activeIndex. If the current\n // index is itself aligned (e.g. 6 with moveItems=3), step back one. If it's not\n // aligned (e.g. 8 with moveItems=3), the nearest aligned below it is floor(activeIndex/moveItems)*moveItems.\n const stepsFromStart = Math.floor(tl.activeIndex / moveItems);\n let prevAlignedIndex;\n if (tl.activeIndex % moveItems === 0) {\n prevAlignedIndex = (stepsFromStart - 1) * moveItems;\n } else {\n prevAlignedIndex = stepsFromStart * moveItems;\n }\n nextActive = Math.max(0, prevAlignedIndex);\n } else if (direction > 0 && tl.activeIndex < maxActiveIndex) {\n // Calculate next aligned step forward\n const stepsFromStart = Math.floor(tl.activeIndex / moveItems);\n const nextAlignedIndex = (stepsFromStart + 1) * moveItems;\n nextActive = Math.min(maxActiveIndex, nextAlignedIndex);\n }\n \n nextActive = clampInt(nextActive, 0, maxActiveIndex);\n\n // Check if navigation would be a no-op\n if (nextActive === tl.activeIndex) {\n return;\n }\n\n // Keep scroll position aligned with the active item so the active node is always centered/visible.\n tl.currentIndex = nextActive;\n tl.activeIndex = nextActive;\n\n // Update arrows based on ACTIVE index (linear user expectation)\n if (tl.activeIndex <= 0) {\n arrowPrev.classList.add('timeline-nav-button--at-start');\n arrowPrev.title = 'Already at beginning of timeline';\n arrowPrev.setAttribute('aria-disabled', 'true');\n } else {\n arrowPrev.classList.remove('timeline-nav-button--at-start');\n arrowPrev.title = 'Go to previous items';\n arrowPrev.setAttribute('aria-disabled', 'false');\n }\n if (tl.activeIndex >= maxActiveIndex) {\n arrowNext.classList.add('timeline-nav-button--at-end');\n arrowNext.title = 'Already at end of timeline';\n arrowNext.setAttribute('aria-disabled', 'true');\n } else {\n arrowNext.classList.remove('timeline-nav-button--at-end');\n arrowNext.title = 'Go to next items';\n arrowNext.setAttribute('aria-disabled', 'false');\n }\n\n // Position by active index so the highlighted item is fully in view (no clipping at edges).\n timelinePosition(tl, tl.activeIndex);\n updateActiveItem(tl, tl.activeIndex);\n\n const activeItem = tl.items[tl.activeIndex];\n const nodeId = activeItem && activeItem.getAttribute('data-node-id');\n if (nodeId) updateDeepLinkUrl(tl.timelineEl, nodeId);\n // Remove focus from button to avoid keyboard navigation side effects\n this.blur();\n };\n \n Array.from(navArrows).forEach((arrow) => {\n arrow.addEventListener('click', handleArrowClick);\n tl.listeners.push({ element: arrow, type: 'click', handler: handleArrowClick });\n });\n }\n\n function setUpHorinzontalTimeline(tl) {\n tl.timelineEl.classList.add('timeline--horizontal');\n // Calculate responsive scaling before layout calculations\n calculateHorizontalScale(tl.timelineEl);\n setHeightandWidths(tl);\n\n // Compute how many items fit in the viewport for this timeline\n const itemsVisible = Math.floor(tl.wrap.offsetWidth / tl.itemWidth) || 1;\n tl.computedVisibleCount = itemsVisible;\n\n if (tl.settings.rtlMode) {\n tl.currentIndex = tl.items.length > itemsVisible ? tl.items.length - itemsVisible : 0;\n } else {\n tl.currentIndex = tl.settings.startIndex;\n }\n\n // Track the \"active\" item separately from the scroll position.\n // Usually these are the same, but they can diverge near the end (e.g. last item).\n tl.activeIndex = tl.currentIndex;\n\n timelinePosition(tl, tl.currentIndex);\n updateActiveItem(tl, tl.activeIndex);\n addNavigation(tl);\n addHorizontalDivider(tl);\n slideTimeline(tl);\n\n const timelineId = tl.timelineEl.id || tl.timelineEl.getAttribute('data-timeline-id');\n if (timelineId) {\n timelineRegistry[timelineId] = {\n setCurrentIndex: function(index) {\n const viewportWidth = tl.wrap.offsetWidth;\n const itemsVisible = Math.floor(viewportWidth / tl.itemWidth);\n const maxScrollIndex = Math.max(0, tl.items.length - itemsVisible);\n // Scroll position clamps to maxIndex, but the active highlight can be the actual requested item.\n tl.activeIndex = Math.max(0, Math.min(index, tl.items.length - 1));\n tl.currentIndex = Math.max(0, Math.min(index, maxScrollIndex));\n },\n updatePosition: function() {\n timelinePosition(tl, tl.currentIndex);\n updateActiveItem(tl, tl.activeIndex);\n const arrowPrev = tl.timelineEl.querySelector('.timeline-nav-button--prev');\n const arrowNext = tl.timelineEl.querySelector('.timeline-nav-button--next');\n if (arrowPrev && arrowNext) {\n const maxActiveIndex = Math.max(0, tl.items.length - 1);\n // Use classes instead of disabled attribute to avoid focus issues\n if (tl.activeIndex <= 0) {\n arrowPrev.classList.add('timeline-nav-button--at-start');\n arrowPrev.title = 'Already at beginning of timeline';\n } else {\n arrowPrev.classList.remove('timeline-nav-button--at-start');\n arrowPrev.title = 'Go to previous items';\n }\n if (tl.activeIndex >= maxActiveIndex) {\n arrowNext.classList.add('timeline-nav-button--at-end');\n arrowNext.title = 'Already at end of timeline';\n } else {\n arrowNext.classList.remove('timeline-nav-button--at-end');\n arrowNext.title = 'Go to next items';\n }\n }\n\n const activeItem = tl.items[tl.activeIndex];\n const nodeId = activeItem && activeItem.getAttribute('data-node-id');\n if (nodeId) updateDeepLinkUrl(tl.timelineEl, nodeId);\n }\n };\n\n // Clicking an item (dot or card) should also make it the current/active node.\n // This runs alongside modal opening.\n const tlData = timelineRegistry[timelineId];\n tl.items.forEach((item, idx) => {\n const activateHandler = () => {\n if (!tl.timelineEl.classList.contains('timeline--horizontal')) return;\n if (tlData && tlData.setCurrentIndex && tlData.updatePosition) {\n tlData.setCurrentIndex(idx);\n tlData.updatePosition();\n }\n };\n item.addEventListener('click', activateHandler);\n tl.listeners.push({ element: item, type: 'click', handler: activateHandler });\n });\n\n // Initialize optional Swiper adapter if requested\n (async function tryInitAdapter(){\n try {\n const useSwiper = tl.settings && (tl.settings.swiperAdapter || tl.settings.useSwiper);\n if (!useSwiper) return;\n\n // If a custom adapter instance/factory provided in settings, use it\n if (tl.settings.swiperAdapter) {\n const provided = tl.settings.swiperAdapter;\n if (typeof provided === 'function') {\n // factory - call to create instance\n tl.adapter = provided();\n } else {\n tl.adapter = provided;\n }\n } else {\n // Fallback to built-in adapter scaffold which will attempt to import 'swiper'\n tl.adapter = new SwiperAdapter();\n }\n\n if (tl.adapter && typeof tl.adapter.init === 'function') {\n await tl.adapter.init(tl.timelineEl, timelineRegistry[timelineId], tl.settings.swiperOptions || {});\n }\n } catch (e) {\n console.warn('Timeline: Swiper adapter initialization failed', e);\n }\n })();\n }\n }\n\n function setUpVerticalTimeline(tl) {\n let lastVisibleIndex = 0;\n tl.items.forEach((item, i) => {\n item.classList.remove('animated', 'fadeIn');\n if (!isElementInViewport(item, tl.settings.verticalTrigger) && i > 0) {\n item.classList.add('animated');\n } else {\n lastVisibleIndex = i;\n }\n // Determine effective vertical start side (left/right) based on sameSideNodes or explicit setting\n const effectiveVSide = resolveSide(tl.settings, 'vertical', tl.settings.rtlMode) || tl.settings.verticalStartPosition;\n const isSameSide = !!resolveSide(tl.settings, 'vertical', tl.settings.rtlMode); // True if sameSideNodes is enabled\n\n if (isSameSide) {\n tl.timelineEl.classList.add('timeline--same-side');\n } else {\n tl.timelineEl.classList.remove('timeline--same-side');\n }\n\n if (isSameSide) {\n // When sameSideNodes is enabled, apply the same side to all items\n if (effectiveVSide === 'right') {\n item.classList.add('timeline__item--right');\n } else {\n item.classList.add('timeline__item--left');\n }\n } else {\n // When sameSideNodes is disabled, alternate based on index\n const divider = effectiveVSide === 'left' ? 1 : 0;\n if (i % 2 === divider && window.innerWidth > tl.settings.minWidth) {\n item.classList.add('timeline__item--right');\n } else {\n item.classList.add('timeline__item--left');\n }\n }\n });\n for (let i = 0; i < lastVisibleIndex; i += 1) {\n tl.items[i].classList.remove('animated', 'fadeIn');\n }\n \n // Use IntersectionObserver instead of scroll listener for better performance\n if ('IntersectionObserver' in window) {\n const observerOptions = {\n rootMargin: tl.settings.verticalTrigger.unit === '%' \n ? `${tl.settings.verticalTrigger.value}%` \n : `${tl.settings.verticalTrigger.value}px`,\n threshold: 0.01\n };\n \n const observer = new IntersectionObserver((entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n entry.target.classList.add('fadeIn');\n }\n });\n }, observerOptions);\n \n tl.items.forEach((item) => {\n if (item.classList.contains('animated')) {\n observer.observe(item);\n }\n });\n \n // Store observer for cleanup\n tl.observer = observer;\n } else {\n // Fallback for older browsers (though we're targeting 2018+)\n const scrollHandler = () => {\n tl.items.forEach((item) => {\n if (isElementInViewport(item, tl.settings.verticalTrigger)) {\n item.classList.add('fadeIn');\n }\n });\n };\n window.addEventListener('scroll', scrollHandler);\n tl.listeners.push({ element: window, type: 'scroll', handler: scrollHandler });\n }\n }\n\n function resetTimelines(tl) {\n // Clean up event listeners\n if (tl.listeners && tl.listeners.length > 0) {\n tl.listeners.forEach(({ element, type, handler }) => {\n element.removeEventListener(type, handler);\n });\n tl.listeners = [];\n }\n \n // Clean up IntersectionObserver\n if (tl.observer) {\n tl.observer.disconnect();\n tl.observer = null;\n }\n // Destroy adapter if present\n if (tl.adapter && typeof tl.adapter.destroy === 'function') {\n try { tl.adapter.destroy(); } catch (e) { /* ignore */ }\n tl.adapter = null;\n }\n \n tl.timelineEl.classList.remove('timeline--horizontal', 'timeline--mobile');\n tl.scroller.removeAttribute('style');\n tl.items.forEach((item) => {\n item.removeAttribute('style');\n item.classList.remove('animated', 'fadeIn', 'timeline__item--left', 'timeline__item--right');\n });\n // Clean up keyboard handlers attached to this timeline\n try { destroyKeyboardForTimeline(tl.timelineEl); } catch (e) { /* ignore */ }\n const navArrows = tl.timelineEl.querySelectorAll('.timeline-nav-button');\n Array.from(navArrows).forEach((arrow) => {\n arrow.parentNode.removeChild(arrow);\n });\n }\n\n function setUpTimelines() {\n timelines.forEach((tl) => {\n tl.timelineEl.style.opacity = 0;\n if (!tl.timelineEl.classList.contains('timeline--loaded')) {\n wrapElements(tl.items);\n }\n resetTimelines(tl);\n if (window.innerWidth <= tl.settings.minWidth) {\n tl.timelineEl.classList.add('timeline--mobile');\n }\n \n // Determine which mode to use based on settings and viewport width\n let useHorizontalMode = false;\n if (tl.settings.mode === 'horizontal' && window.innerWidth > tl.settings.minWidth) {\n useHorizontalMode = true;\n } else if (tl.settings.mode === 'vertical' && window.innerWidth > tl.settings.maxWidth) {\n useHorizontalMode = true;\n }\n \n if (useHorizontalMode) {\n setUpHorinzontalTimeline(tl);\n } else {\n setUpVerticalTimeline(tl);\n }\n tl.timelineEl.classList.add('timeline--loaded');\n\n // Apply layout fallbacks for missing images and summaries\n initLayoutFallbacks(tl.timelineEl);\n\n // Emit an initialization event for this timeline so other components can react\n try {\n const timelineId = tl.timelineEl.id || tl.timelineEl.getAttribute('data-timeline-id');\n const detail = { id: timelineId, settings: tl.settings, api: timelineRegistry[timelineId] };\n const ev = new CustomEvent('timeline:initialized', { detail });\n try { tl.timelineEl.dispatchEvent(ev); } catch (e) { /* ignore */ }\n try { document.dispatchEvent(new CustomEvent('timeline:initialized', { detail })); } catch (e) { /* ignore */ }\n } catch (e) {\n // Non-fatal: continue silently\n }\n });\n\n setTimeout(() => {\n timelines.forEach((tl) => {\n tl.timelineEl.style.opacity = 1;\n });\n }, 500);\n }\n\n try {\n setUpTimelines();\n // After timelines are set up, run deep-link handler so manual (inline) inits respond to URL params\n try { handleDeepLinking(); } catch (e) { /* ignore */ }\n // Attach resize handler (stored so it can be removed in tests)\n const resizeHandler = () => {\n clearTimeout(resizeTimer);\n resizeTimer = setTimeout(() => {\n const newWinWidth = window.innerWidth;\n if (newWinWidth !== winWidth) {\n setUpTimelines();\n winWidth = newWinWidth;\n }\n }, 250);\n };\n window.addEventListener('resize', resizeHandler);\n\n // Expose a test helper to allow tests to perform cleanup of timelines and listeners\n // This is intentionally a non-public API for testing only.\n timeline._test_destroyAll = function() {\n // Clean up each timeline\n timelines.forEach((tl) => {\n try {\n resetTimelines(tl);\n } catch (e) {\n // ignore\n }\n });\n // Clear timelines array\n timelines.length = 0;\n // Clear any pending resize timer\n if (resizeTimer) {\n clearTimeout(resizeTimer);\n resizeTimer = null;\n }\n // Remove resize handler\n try { window.removeEventListener('resize', resizeHandler); } catch (e) { /* ignore */ }\n };\n } catch (e) {\n console.error('Timeline initialization failed:', e);\n }\n}","/**\n * JSON data loading and timeline rendering\n *\n * Handles JSON fetching with caching, data normalization, DOM rendering, and timeline initialization.\n * Includes Lorem Ipsum fallbacks and image error handling.\n */\n\nimport { timelineBasePath } from '../shared/config.js';\nimport { showTimelineError } from './error-ui.js';\nimport { applyTimelineColors } from './colors.js';\nimport { handleDeepLinking } from './deep-linking.js';\nimport { timeline } from '../core/timeline-engine.js';\nimport { LOREM_PARAGRAPH, LOREM_FULL } from '../shared/lipsum.js';\nimport { formatAccessibleDate } from '../shared/utils.js';\n\n/**\n * Normalize raw item data to standard timeline schema\n *\n * Converts various data formats to consistent 5-field structure.\n * Uses Lorem Ipsum placeholders for missing fields.\n *\n * @param {Object} rawData - Raw item data from JSON or user\n * @param {string} [rawData.id] - Optional unique identifier\n * @param {string} [rawData.date] - Date label (any format)\n * @param {string} [rawData.heading] - Item title\n * @param {string} [rawData.summary] - Short text (shown in node)\n * @param {string} [rawData.content] - Detailed HTML content (shown in modal)\n * @param {string} [rawData.image] - Image URL (shown in node and modal)\n * @returns {Object} Normalized item object with all 5+ fields populated\n */\nexport function normalizeItemData(rawData) {\n const normalized = {\n id: rawData.id || null,\n date: rawData.date || 'DD/MM/YYYY',\n heading: rawData.heading || 'Node Title',\n summary: rawData.summary || LOREM_PARAGRAPH,\n content: rawData.content || LOREM_FULL,\n image: rawData.image || null\n };\n \n return normalized;\n}\n\n/**\n * Sanitize HTML content for safe display in timeline\n *\n * Removes dangerous tags (script, form, input, h1, h2) while preserving safe markup.\n * Allowed tags: p, strong, em, u, a, ul, ol, li, br, blockquote, h3-h6\n *\n * @param {string} html - Raw HTML string\n * @returns {string} Sanitized HTML safe for DOM injection\n */\nexport function sanitizeContent(html) {\n if (!html) return '';\n \n let clean = html;\n \n // Remove script tags and content\n clean = clean.replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '');\n \n // Remove form tags and content\n clean = clean.replace(/<form[^>]*>[\\s\\S]*?<\\/form>/gi, '');\n \n // Remove input tags\n clean = clean.replace(/<input[^>]*>/gi, '');\n \n // Remove h1 tags (replace with h3)\n clean = clean.replace(/<h1/gi, '<h3');\n clean = clean.replace(/<\\/h1>/gi, '</h3>');\n \n // Remove h2 tags (replace with h3)\n clean = clean.replace(/<h2/gi, '<h3');\n clean = clean.replace(/<\\/h2>/gi, '</h3>');\n \n return clean.trim();\n}\n\n/**\n * Create a DOM element for a timeline item\n *\n * Builds the DOM structure for a single timeline node from normalized item data.\n * Creates:\n * - Date label\n * - Optional image (with fallback to missing-image.svg on error)\n * - Heading\n * - Summary (body text, truncated by CSS)\n * - Optional inline modal content (for detailed view)\n *\n * @param {Object} item - Normalized item data (from normalizeItemData)\n * @returns {HTMLElement} Complete DOM element ready to insert into timeline\n */\nexport function createItemNode(item) {\n const normalized = normalizeItemData(item);\n \n const itemEl = document.createElement('div');\n itemEl.className = 'timeline__item';\n itemEl.setAttribute('role', 'listitem');\n itemEl.setAttribute('tabindex', '0');\n \n if (normalized.id) {\n itemEl.setAttribute('data-node-id', normalized.id);\n if (!itemEl.id) {\n try {\n itemEl.id = String(normalized.id);\n } catch (e) {\n /* ignore invalid id assignments */\n }\n }\n }\n\n // Timeline content (visible in node)\n const content = document.createElement('div');\n content.className = 'timeline__content';\n\n // Date\n const dateEl = document.createElement('div');\n dateEl.className = 'timeline__date';\n dateEl.textContent = normalized.date;\n content.appendChild(dateEl);\n\n // Image (optional)\n if (normalized.image) {\n const img = document.createElement('img');\n img.src = normalized.image;\n img.className = 'timeline__image';\n img.alt = normalized.heading || 'Timeline image';\n img.loading = 'lazy';\n img.onerror = function() {\n console.error('Timeline: Image failed to load:', normalized.image);\n this.src = timelineBasePath + '/missing-image.svg';\n this.alt = 'Image not found';\n };\n content.appendChild(img);\n }\n\n // Heading\n const headingEl = document.createElement('h3');\n headingEl.className = 'timeline__heading';\n headingEl.textContent = normalized.heading;\n content.appendChild(headingEl);\n\n // Summary (truncated by CSS)\n const summaryEl = document.createElement('div');\n summaryEl.className = 'timeline__summary';\n summaryEl.textContent = normalized.summary;\n content.appendChild(summaryEl);\n\n itemEl.appendChild(content);\n\n // Accessibility: create a visually-hidden label and reference it via aria-labelledby\n // Priority: explicit ariaLabel in item data (ariaLabel or \"aria-label\"), then generate from date+heading\n const explicitLabel = item && (item.ariaLabel || item['aria-label'] || item.aria_label);\n let labelText = '';\n if (explicitLabel && String(explicitLabel).trim() !== '') {\n labelText = String(explicitLabel).trim();\n } else {\n const formattedDate = formatAccessibleDate(normalized.date);\n labelText = `Date: ${formattedDate}. Title: ${normalized.heading}`;\n if (normalized.id) labelText = `Node ${normalized.id}: ` + labelText;\n }\n if (labelText) {\n const labelId = 'tl-label-' + Math.random().toString(36).slice(2, 9);\n const sr = document.createElement('span');\n sr.className = 'sr-only';\n sr.id = labelId;\n sr.textContent = labelText;\n itemEl.appendChild(sr);\n itemEl.setAttribute('aria-labelledby', labelId);\n }\n\n // Modal content (hidden by default, shown in popup)\n const modalContent = document.createElement('div');\n modalContent.className = 'timeline__modal-content';\n modalContent.innerHTML = sanitizeContent(normalized.content);\n itemEl.appendChild(modalContent);\n\n // Click handler to open modal\n itemEl.addEventListener('click', function(e) {\n e.preventDefault();\n if (typeof window.openTimelineModal === 'function') {\n window.openTimelineModal(itemEl);\n }\n });\n\n itemEl.addEventListener('keydown', function(e) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n if (typeof window.openTimelineModal === 'function') {\n window.openTimelineModal(itemEl);\n }\n }\n });\n\n return itemEl;\n}\n\n/**\n * Apply configuration as data attributes\n */\nfunction applyDataAttributes(container, config) {\n if (config.layoutMode) {\n container.setAttribute('data-mode', config.layoutMode);\n }\n if (config.moveItems !== undefined) {\n container.setAttribute('data-move-items', config.moveItems);\n }\n if (config.minWidth !== undefined) {\n container.setAttribute('data-min-width', config.minWidth);\n }\n if (config.maxWidth !== undefined) {\n container.setAttribute('data-max-width', config.maxWidth);\n }\n /* Additional mappings so JSON config becomes authoritative (JSON > JS > HTML)\n These attributes are the keys the timeline engine reads from dataset. */\n if (config.startIndex !== undefined) {\n container.setAttribute('data-start-index', config.startIndex);\n }\n if (config.horizontalStartPosition !== undefined) {\n container.setAttribute('data-horizontal-start-position', config.horizontalStartPosition);\n }\n if (config.verticalStartPosition !== undefined) {\n container.setAttribute('data-vertical-start-position', config.verticalStartPosition);\n }\n if (config.verticalTrigger !== undefined) {\n container.setAttribute('data-vertical-trigger', config.verticalTrigger);\n }\n if (config.rtlMode !== undefined) {\n container.setAttribute('data-rtl-mode', String(config.rtlMode));\n }\n if (config.nodeColor !== undefined) {\n container.setAttribute('data-node-color', config.nodeColor);\n }\n if (config.lineColor !== undefined) {\n container.setAttribute('data-line-color', config.lineColor);\n }\n if (config.navColor !== undefined) {\n container.setAttribute('data-nav-color', config.navColor);\n }\n if (config.sameSideNodes !== undefined) {\n container.setAttribute('data-same-side-nodes', config.sameSideNodes);\n }\n if (config.useSwiper !== undefined) {\n container.setAttribute('data-use-swiper', config.useSwiper);\n }\n}\n\n/**\n * Render timeline from data array\n *\n * Constructs the complete timeline DOM structure from an array of item data.\n * Creates .timeline__wrap and .timeline__items containers if missing.\n * Applies configuration (colors, options, heading) to the container.\n * Initializes timeline after rendering.\n *\n * @param {string} containerSelector - CSS selector for timeline container element\n * @param {Array<Object>} data - Array of item objects (raw or normalized)\n * @param {Object} [config] - Configuration object\n * @param {string} [config.timelineName] - Heading to display above timeline\n * @param {string} [config.mode] - 'horizontal' or 'vertical'\n * @param {string} [config.nodeColor] - CSS color for nodes\n * @param {string} [config.lineColor] - CSS color for line\n * @param {string} [config.navColor] - CSS color for nav\n * @param {Record<string, any>} [config.otherSettings] - Any other timeline options (see timeline-engine.js)\n * @returns {HTMLElement|null} - Timeline container element, or null if container not found\n */\nexport function renderTimelineFromData(containerSelector, data, config) {\n const container = document.querySelector(containerSelector);\n if (!container) {\n console.error('Timeline: Container not found:', containerSelector);\n return null;\n }\n\n // Remove any previous error state\n container.classList.remove('timeline--error');\n\n // Get or create timeline structure\n let itemsWrap = container.querySelector('.timeline__items');\n if (!itemsWrap) {\n const wrap = document.createElement('div');\n wrap.className = 'timeline__wrap';\n itemsWrap = document.createElement('div');\n itemsWrap.className = 'timeline__items';\n itemsWrap.setAttribute('role', 'list');\n wrap.appendChild(itemsWrap);\n container.appendChild(wrap);\n } else {\n itemsWrap.innerHTML = '';\n itemsWrap.setAttribute('role', 'list');\n }\n\n // Apply configuration\n if (config) {\n applyDataAttributes(container, config);\n applyTimelineColors(container, config);\n\n // Add timeline heading if provided\n if (config.timelineName && config.timelineName.trim() !== '') {\n const existingHeading = container.previousElementSibling;\n if (existingHeading && existingHeading.classList.contains('timeline__title')) {\n existingHeading.textContent = config.timelineName;\n } else {\n const heading = document.createElement('h1');\n heading.className = 'timeline__title';\n heading.textContent = config.timelineName;\n container.parentNode.insertBefore(heading, container);\n }\n container.setAttribute('data-timeline-name', config.timelineName);\n }\n }\n\n // Create timeline items from data\n if (!Array.isArray(data) || data.length === 0) {\n showTimelineError(container, 'no-data', 'No timeline items provided');\n return null;\n }\n\n data.forEach(function(item) {\n itemsWrap.appendChild(createItemNode(item));\n });\n\n return container;\n}\n\n/**\n * Initialize timeline from provided data\n */\n/**\n * Create and initialize timeline from data array\n *\n * Convenience function that combines renderTimelineFromData + timeline initialization.\n * Waits for lazy-loaded images before calculating layout to avoid dimension errors.\n *\n * @param {string} containerSelector - CSS selector for timeline container\n * @param {Array<Object>} data - Array of item objects\n * @param {Object} [options] - Timeline configuration (same as renderTimelineFromData)\n * @returns {void}\n */\nexport function timelineFromData(containerSelector, data, options) {\n const container = renderTimelineFromData(containerSelector, data, options);\n if (!container) return;\n // Wait for images to settle before running layout to avoid height miscalculations\n waitForImages(container).then(() => {\n timeline([container], options || {});\n });\n}\n\n/**\n * Fetch JSON data from URL, cache it, and initialize timeline\n *\n * Fetches timeline data from a JSON file with localStorage caching.\n * Shows loading spinner during fetch. Auto-initializes timeline after data loads.\n * Handles errors gracefully with error UI display.\n *\n * Cache validation: Stored with timestamp, expires after 1 hour.\n *\n * @param {string} url - URL to JSON file containing timeline data array\n * @param {string} containerSelector - CSS selector for timeline container\n * @returns {void}\n */\nexport function loadDataFromJson(url, containerSelector) {\n const container = document.querySelector(containerSelector);\n if (!container) {\n console.error('Timeline: Container not found:', containerSelector);\n showTimelineError(null, 'missing-element', 'Timeline container not found: ' + containerSelector);\n return;\n }\n\n // Resolve URL against current page to avoid accidentally using a stale base/origin\n const resolvedUrl = (function() {\n try {\n return new URL(url, window.location.href).href;\n } catch (e) {\n return url;\n }\n })();\n\n console.info('Timeline: loading JSON from', resolvedUrl, '(from attribute', url + ')');\n\n // Check cache first\n const cacheKey = 'timeline_cache_' + resolvedUrl;\n let cachedData = null;\n let cachedTime = null;\n\n if (typeof(Storage) !== 'undefined') {\n try {\n const cached = localStorage.getItem(cacheKey);\n if (cached) {\n const parsedCache = JSON.parse(cached);\n cachedData = parsedCache.data;\n cachedTime = parsedCache.timestamp;\n }\n } catch (e) {\n console.warn('Timeline: Failed to parse cached data');\n }\n }\n\n // Fetch JSON\n fetch(resolvedUrl)\n .then(response => {\n if (!response.ok) throw new Error('Failed to load: ' + response.statusText);\n return response.json();\n })\n .then(jsonData => {\n // Check if we need to update cache\n const needsUpdate = !cachedData || \n !jsonData.lastupdated || \n new Date(jsonData.lastupdated) > new Date(cachedTime);\n\n if (needsUpdate && jsonData.nodes && typeof(Storage) !== 'undefined') {\n try {\n localStorage.setItem(cacheKey, JSON.stringify({\n data: jsonData.nodes,\n timestamp: jsonData.lastupdated || new Date().toISOString()\n }));\n } catch (e) {\n console.warn('Timeline: Unable to cache data', e);\n }\n }\n\n const dataToUse = jsonData.nodes || jsonData;\n const config = Object.assign({}, jsonData);\n delete config.nodes;\n\n renderTimelineFromData(containerSelector, dataToUse, config);\n // Ensure images finish loading before calling the layout engine so heights are correct\n waitForImages(container).then(() => {\n timeline([container], {});\n showTimelineTitle(container);\n handleDeepLinking();\n });\n })\n .catch(error => {\n console.error('Timeline: Error loading JSON:', error);\n \n // Try to use cached data as fallback\n if (cachedData && cachedData.length > 0) {\n console.log('Timeline: Using cached data as fallback');\n renderTimelineFromData(containerSelector, cachedData, {});\n waitForImages(container).then(() => {\n timeline([container], {});\n showTimelineTitle(container);\n });\n } else {\n showTimelineError(container, 'load-failed', 'Failed to load timeline data');\n }\n });\n}\n\n/**\n * Wait for all images inside a container to either load or error.\n * Resolves immediately if there are no images.\n */\nfunction waitForImages(container) {\n if (!container) return Promise.resolve();\n const imgs = Array.from(container.querySelectorAll('img'));\n if (imgs.length === 0) return Promise.resolve();\n\n return new Promise((resolve) => {\n let remaining = imgs.length;\n\n const checkDone = () => {\n remaining -= 1;\n if (remaining <= 0) resolve();\n };\n\n imgs.forEach((img) => {\n if (img.complete) {\n checkDone();\n } else {\n const onSettled = () => {\n img.removeEventListener('load', onSettled);\n img.removeEventListener('error', onSettled);\n checkDone();\n };\n img.addEventListener('load', onSettled);\n img.addEventListener('error', onSettled);\n }\n });\n\n // Fallback safety: resolve after 1s even if some images hang\n setTimeout(() => resolve(), 1000);\n });\n}\n\n/**\n * Show the timeline title (previous sibling with .timeline__title class)\n * This is called after the timeline layout is complete to fade it in.\n */\nfunction showTimelineTitle(container) {\n if (!container) return;\n const title = container.previousElementSibling;\n if (title && title.classList.contains('timeline__title')) {\n // Trigger reflow to ensure transition works\n title.offsetHeight;\n title.classList.add('timeline__title--visible');\n }\n}\n\n/**\n * Clear cached JSON data for a specific URL or all timeline caches\n *\n * Removes cached data from localStorage. If URL provided, clears only that URL's cache.\n * If no URL provided, clears all timeline caches (keys starting with 'timeline_cache_').\n *\n * @param {string} [url] - URL key to clear, or omit to clear all timeline caches\n * @returns {void}\n */\nexport function clearTimelineCache(url) {\n if (typeof(Storage) === 'undefined') {\n console.warn('Timeline: localStorage not supported');\n return;\n }\n\n if (url) {\n const cacheKey = 'timeline_cache_' + url;\n localStorage.removeItem(cacheKey);\n console.log('Timeline: Cleared cache for', url);\n } else {\n // Clear all timeline caches\n const keys = Object.keys(localStorage);\n let cleared = 0;\n keys.forEach(key => {\n if (key.startsWith('timeline_cache_')) {\n localStorage.removeItem(key);\n cleared++;\n }\n });\n console.log('Timeline: Cleared', cleared, 'cache(s)');\n }\n}\n\n/**\n * Process and render timeline data (alias for renderTimelineFromData)\n *\n * Alternative API for rendering timeline. Equivalent to renderTimelineFromData.\n *\n * @param {string} containerSelector - CSS selector for timeline container\n * @param {Array<Object>} data - Array of item objects\n * @param {Object} [config] - Timeline configuration\n * @returns {HTMLElement|null} - Timeline container element, or null if container not found\n */\nexport function processTimelineData(containerSelector, data, config) {\n return renderTimelineFromData(containerSelector, data, config);\n}\n","/**\n * Lorem Ipsum placeholder text utility\n * Provides reusable placeholder content for timeline nodes\n */\n\n// Full paragraph of Lorem Ipsum\nconst LOREM_PARAGRAPH = 'Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est aliquip amet voluptate voluptate dolor minim nulla est proident.';\n\n// Short sentence\nconst LOREM_SHORT = 'Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.';\n\n// Multiple paragraphs\nconst LOREM_FULL = `<p>Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est aliquip amet voluptate voluptate dolor minim nulla est proident. Nostrud officia pariatur ut officia. Sit irure elit esse ea nulla sunt ex occaecat reprehenderit commodo officia dolor Lorem duis laboris cupidatat officia voluptate.</p><p>Culpa proident adipisicing id nulla nisi laboris ex in Lorem sunt duis officia eiusmod. Aliqua reprehenderit commodo ex non excepteur duis sunt velit enim. Voluptate laboris sint cupidatat ullamco ut ea consectetur et est culpa et culpa duis.</p>`;\n\nexport { LOREM_PARAGRAPH, LOREM_SHORT, LOREM_FULL };\n","/**\r\n * timeline.js - Vanilla JavaScript timeline library\r\n *\r\n * A responsive timeline component for displaying chronological events with support\r\n * for horizontal/vertical layouts, JSON data loading, color theming, modals, and deep linking.\r\n *\r\n * Original author: Mike Collins (https://github.com/squarechip/timeline) - 2018\r\n * Maintained by: Ken Dawson (https://github.com/kendawson-online) - 2026\r\n *\r\n * Features:\r\n * - Responsive horizontal/vertical layouts with breakpoint switching\r\n * - JSON data loading with caching\r\n * - Modal popups for detailed item viewing\r\n * - Color customization and accessibility\r\n * - Deep linking (URL-based navigation to specific timeline items)\r\n * - Optional Swiper carousel integration\r\n * - jQuery plugin support (if jQuery available)\r\n */\r\n\r\nimport { createTimelineModal, openTimelineModal, closeTimelineModal, destroyTimelineModal } from './features/modals.js';\r\nimport { handleDeepLinking, navigateToNodeIndex } from './features/deep-linking.js';\r\nimport { renderTimelineFromData, timelineFromData, loadDataFromJson, processTimelineData, clearTimelineCache } from './features/data-loader.js';\r\nimport './features/keyboard.js';\r\nimport { destroyKeyboardForTimeline } from './features/keyboard.js';\r\nimport { timeline } from './core/timeline-engine.js';\r\nimport '../css/timeline.css';\r\n\r\n/**\r\n * Auto-initialize timelines that have [data-json-config] attribute\r\n * Queries DOM for elements with data-json-config and loads JSON data for each\r\n * @private\r\n */\r\nfunction autoInitJsonTimelines() {\r\n const timelinesWithJson = document.querySelectorAll('[data-json-config]');\r\n timelinesWithJson.forEach(function(timelineEl) {\r\n const jsonUrl = timelineEl.getAttribute('data-json-config');\r\n if (!jsonUrl) return;\r\n const className = (timelineEl.className || '').split(' ')[0];\r\n const selector = timelineEl.id ? '#' + timelineEl.id : (className ? '.' + className : null);\r\n if (selector) {\r\n loadDataFromJson(jsonUrl, selector);\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Expose all timeline functions to global scope (window object)\r\n * Allows users to access timeline API from console and global code\r\n * @private\r\n */\r\nfunction exposeGlobals() {\r\n if (typeof window === 'undefined') return;\r\n window.timeline = timeline;\r\n window.destroyTimelines = destroyTimelines;\r\n window.timelineFromData = timelineFromData;\r\n window.renderTimelineFromData = renderTimelineFromData;\r\n window.processTimelineData = processTimelineData;\r\n window.loadDataFromJson = loadDataFromJson;\r\n window.clearTimelineCache = clearTimelineCache;\r\n window.createTimelineModal = createTimelineModal;\r\n window.openTimelineModal = openTimelineModal;\r\n window.closeTimelineModal = closeTimelineModal;\r\n window.handleTimelineDeepLinking = handleDeepLinking;\r\n window.navigateTimelineToNodeIndex = navigateToNodeIndex;\r\n}\r\n\r\nexposeGlobals();\r\n\r\n/**\r\n * Auto-initialize JSON-configured timelines when DOM is ready\r\n */\r\ndocument.addEventListener('DOMContentLoaded', function() {\r\n autoInitJsonTimelines();\r\n});\r\n\r\n/**\r\n * Destroy timelines and clean up listeners and modals\r\n * Useful for SPA teardown or reinitialization flows.\r\n * @returns {void}\r\n */\r\nfunction destroyTimelines() {\r\n if (timeline && typeof timeline._test_destroyAll === 'function') {\r\n try { timeline._test_destroyAll(); } catch (_) { /* ignore */ }\r\n }\r\n try {\r\n const timelines = document.querySelectorAll('.timeline');\r\n timelines.forEach((tl) => destroyKeyboardForTimeline(tl));\r\n } catch (_) {\r\n /* ignore */\r\n }\r\n try { destroyTimelineModal(); } catch (_) { /* ignore */ }\r\n}\r\n\r\n/**\r\n * Register jQuery plugin if jQuery is available\r\n * Allows usage: $('#timeline').timeline({ options });\r\n */\r\nif (typeof window !== 'undefined' && window.jQuery) {\r\n (($) => {\r\n /**\r\n * jQuery plugin for timeline initialization\r\n * @param {Object} opts - Timeline configuration options\r\n * @returns {jQuery} - Returns jQuery collection for chaining\r\n */\r\n $.fn.timeline = function(opts) {\r\n timeline(this, opts);\r\n return this;\r\n };\r\n })(window.jQuery);\r\n}\r\n\r\nexport {\r\n timeline,\r\n destroyTimelines,\r\n timelineFromData,\r\n renderTimelineFromData,\r\n loadDataFromJson,\r\n processTimelineData,\r\n clearTimelineCache,\r\n createTimelineModal,\r\n openTimelineModal,\r\n closeTimelineModal,\r\n destroyTimelineModal,\r\n handleDeepLinking,\r\n navigateToNodeIndex\r\n};"],"names":["modalState","modal","overlay","timelineRegistry","lastFocusedElement","escKeyHandler","getFocusableElements","container","focusable","querySelectorAll","Array","from","filter","el","hasAttribute","trapFocus","e","key","length","first","last","active","document","activeElement","shiftKey","contains","preventDefault","focus","createTimelineModal","createElement","className","addEventListener","closeTimelineModal","setAttribute","titleId","innerHTML","closeBtn","querySelector","closeBottomBtn","stopPropagation","body","appendChild","classList","openTimelineModal","itemEl","title","getAttribute","content","image","html","modalTitle","modalText","modalImage","textContent","src","alt","style","display","domModal","trim","replace","setTimeout","add","overflow","firstFocusable","remove","destroyTimelineModal","removeEventListener","_","parentNode","removeChild","handleDeepLinking","containerSelector","urlParams","URLSearchParams","window","location","search","timelineId","get","nodeId","targetContainer","getElementById","console","warn","scrollIntoView","behavior","block","targetNode","byId","forEach","n","itemIndex","children","indexOf","navigateToNodeIndex","id","String","hash","index","tlData","setCurrentIndex","updatePosition","timelineBasePath","TimelineConfig","basePath","scripts","getElementsByTagName","i","dir","substring","lastIndexOf","showTimelineError","errorType","details","errorInfo","message","solution","errorDiv","errorIcon","onload","info","this","naturalWidth","onerror","width","height","errorTitle","errorMessage","errorSolution","errorDetails","error","evt","CustomEvent","detail","type","containerId","dispatchEvent","getColorBrightness","color","rgb","startsWith","hex","parseInt","matches","match","map","Number","formatAccessibleDate","dateString","locale","navigator","language","parsed","Date","parse","isNaN","parts","split","p","mm","dd","yy","d","getTime","formatDateWithOrdinal","dateObj","fmt","Intl","DateTimeFormat","month","day","year","prototype","formatToParts","value","dayNum","getDate","s","v","ordinalSuffix","format","toDateString","applyTimelineColors","config","nodeColor","lineColor","navColor","setProperty","arrowColor","SwiperAdapter","constructor","swiper","_container","_original","init","timelineEl","timelineApi","options","SwiperLib","swiperCdn","mod","import","default","Swiper","seen","Storage","localStorage","getItem","log","setItem","items","itemsClass","itemClasses","child","push","defaultOpts","Object","assign","slidesPerView","freeMode","spaceBetween","direction","slideTo","opts","speed","slideBy","delta","activeIndex","update","destroy","undefined","announceActiveNode","region","labelId","announcement","labelEl","date","heading","initializeKeyboardForTimeline","api","prev","next","tab","tabIndex","item","setSequentialTabOrder","existing","__keyboardHandlers","keydown","prevKey","nextKey","keydownHandler","click","prevKeyHandler","nextKeyHandler","navClickHandler","globalKeyHandler","tag","target","tagName","toLowerCase","isContentEditable","globalKey","navClick","itemKeyHandler","includes","all","currentIndex","currentTarget","targetIndex","Math","max","min","itemKey","bindHotkeys","createAriaLiveRegion","destroyKeyboardForTimeline","handlers","applyNoImageFallback","root","c","img","complete","once","applyNoSummaryFallback","summary","initLayoutFallbacks","MutationObserver","observe","childList","subtree","enhanceInlineItems","firstP","ensureInlineModalData","dataLabel","dataset","ariaLabel","labelText","dateEl","headingEl","dateText","headingText","formattedDate","idAttr","random","toString","slice","sr","ensureAccessibleLabelForInlineItem","createArrowSVG","clampInt","updateDeepLinkUrl","history","params","has","timelineParam","shouldUpdateDeepLinkForTimeline","url","URL","href","searchParams","set","replaceState","timeline","collection","timelines","warningLabel","resizeTimer","winWidth","innerWidth","defaultSettings","minWidth","defaultValue","maxWidth","horizontalStartPosition","acceptedValues","mode","moveItems","rtlMode","startIndex","verticalStartPosition","verticalTrigger","useSwiper","sameSideNodes","resolveSide","settings","rtl","vDefault","itemWrap","wrapper","classes","insertBefore","isElementInViewport","triggerPosition","rect","getBoundingClientRect","windowHeight","innerHeight","documentElement","clientHeight","defaultTrigger","triggerUnit","unit","triggerValue","trigger","top","left","clientWidth","addTransforms","transform","webkitTransform","msTransform","timelinePosition","tl","safeIndex","originalLeft","offsetLeft","timelineStyle","getComputedStyle","paddingLeft","paddingRight","viewportWidth","wrap","offsetWidth","itemWidth","position","minPositionForRight","str","scroller","updateActiveItem","setUpHorinzontalTimeline","constraints","availableHeight","requiredHeight","scaleFactor","scaleValue","scaled","round","calculateHorizontalScale","computedStyle","nodeWidth","getPropertyValue","scrollerWidth","setWidths","oddIndexTallest","evenIndexTallest","offsetHeight","transformString","effectiveHSide","isSameSide","tallestHeight","computedVisibleCount","floor","setHeights","setHeightandWidths","itemsVisible","prevArrow","nextArrow","topPosition","maxActiveIndex","addNavigation","divider","horizontalDivider","addHorizontalDivider","navArrows","arrowPrev","arrowNext","handleArrowClick","stopImmediatePropagation","nextActive","stepsFromStart","prevAlignedIndex","nextAlignedIndex","activeItem","blur","arrow","listeners","element","handler","slideTimeline","maxScrollIndex","idx","activateHandler","swiperAdapter","provided","adapter","swiperOptions","resetTimelines","observer","disconnect","removeAttribute","setUpTimelines","opacity","useHorizontalMode","lastVisibleIndex","effectiveVSide","observerOptions","rootMargin","threshold","IntersectionObserver","entries","entry","isIntersecting","scrollHandler","setUpVerticalTimeline","ev","timelineName","errorPart","data","Error","call","keys","candidate","minwidth","forceVerticalMode","forceverticalmode","maxwidth","settingName","getData","k","triggerArray","triggerValid","useswiper","val","now","substr","resizeHandler","clearTimeout","newWinWidth","_test_destroyAll","tlEl","renderTimelineFromData","itemsWrap","layoutMode","applyDataAttributes","existingHeading","previousElementSibling","isArray","normalized","rawData","loading","summaryEl","explicitLabel","aria_label","modalContent","clean","sanitizeContent","createItemNode","timelineFromData","waitForImages","then","loadDataFromJson","resolvedUrl","cacheKey","cachedData","cachedTime","cached","parsedCache","JSON","timestamp","fetch","response","ok","statusText","json","jsonData","lastupdated","nodes","stringify","toISOString","dataToUse","showTimelineTitle","catch","Promise","resolve","imgs","remaining","checkDone","onSettled","clearTimelineCache","removeItem","cleared","processTimelineData","destroyTimelines","handleTimelineDeepLinking","navigateTimelineToNodeIndex","jsonUrl","selector","jQuery","fn"],"mappings":"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"}