(html: string)
| 142 | * these via ffprobe / el.duration and call injectDurations(). |
| 143 | */ |
| 144 | export function compileTimingAttrs(html: string): CompilationResult { |
| 145 | const unresolved: UnresolvedElement[] = []; |
| 146 | let nextVideoId = 0; |
| 147 | let nextAudioId = 0; |
| 148 | |
| 149 | // Process <video ...> tags |
| 150 | html = html.replace(/<video[^>]*>/gi, (match) => { |
| 151 | const { tag, unresolved: u } = compileTag(match, true, () => nextVideoId++); |
| 152 | if (u) unresolved.push(u); |
| 153 | return tag; |
| 154 | }); |
| 155 | |
| 156 | // Process <audio ...> tags |
| 157 | html = html.replace(/<audio[^>]*>/gi, (match) => { |
| 158 | const { tag, unresolved: u } = compileTag(match, false, () => nextAudioId++); |
| 159 | if (u) unresolved.push(u); |
| 160 | return tag; |
| 161 | }); |
| 162 | |
| 163 | // Identify unresolved timed elements (divs with data-start but no data-end/data-duration) |
| 164 | // These are typically compositions whose duration depends on GSAP timelines |
| 165 | html.replace(/<(?:div|section)[^>]*>/gi, (match) => { |
| 166 | if (!hasAttr(match, "data-start")) return match; |
| 167 | if (hasAttr(match, "data-end") || hasAttr(match, "data-duration")) return match; |
| 168 | |
| 169 | const id = getAttr(match, "id"); |
| 170 | const compositionSrc = getAttr(match, "data-composition-src"); |
| 171 | if (id) { |
| 172 | const startStr = getAttr(match, "data-start"); |
| 173 | unresolved.push({ |
| 174 | id, |
| 175 | tagName: "div", |
| 176 | start: startStr ? parseFloat(startStr) : 0, |
| 177 | mediaStart: 0, |
| 178 | compositionSrc: compositionSrc ?? undefined, |
| 179 | }); |
| 180 | } |
| 181 | |
| 182 | return match; |
| 183 | }); |
| 184 | |
| 185 | return { html, unresolved }; |
| 186 | } |
| 187 | |
| 188 | /** |
| 189 | * Inject resolved durations into compiled HTML. |
no test coverage detected