Add IETP multimedia embedding with responsive video, audio, and interactive images#159
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…udio, and interactive image support Co-authored-by: AmedeoPelliccia <164860269+AmedeoPelliccia@users.noreply.github.com>
…de with multimedia section Co-authored-by: AmedeoPelliccia <164860269+AmedeoPelliccia@users.noreply.github.com>
…on notes, enhance accessibility Co-authored-by: AmedeoPelliccia <164860269+AmedeoPelliccia@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR implements interactive multimedia embedding for the IETP (Interactive Electronic Technical Publication) system, enabling S1000D data modules to render video, audio, and images with responsive controls and accessibility features.
Changes:
- Added new MediaEmbed JavaScript component for auto-detecting and enhancing multimedia elements
- Implemented responsive CSS styling with dark theme support and accessibility features
- Extended build system to render S1000D multimedia object references into HTML
- Added comprehensive documentation for multimedia features and usage guidelines
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
multimedia/videos/README.md |
Documentation for video content standards and S1000D naming conventions |
multimedia/graphics/README.md |
Guidelines for technical graphics and diagrams |
ietp/build_ietp.py |
Added render_multimedia_object() function to convert S1000D multimedia elements to HTML |
ietp/assets/js/media-embed.js |
New JavaScript component for multimedia embedding with zoom, play controls, and responsive behavior |
ietp/assets/css/media-embed.css |
Styling for multimedia components with responsive design and accessibility support |
ietp/README.md |
Updated documentation to reference new multimedia features |
docs/user-guide/modules/multimedia-embedding.md |
Comprehensive guide for multimedia embedding features |
docs/user-guide/User-Guide.md |
Added link to multimedia embedding documentation |
| modal.innerHTML = ` | ||
| <div class="modal-backdrop"></div> | ||
| <div class="modal-content"> | ||
| <button class="modal-close" aria-label="Close">×</button> | ||
| <img src="${img.src}" alt="${img.alt}"> | ||
| </div> | ||
| `; |
There was a problem hiding this comment.
Direct interpolation of img.src and img.alt into HTML template strings creates potential XSS vulnerabilities if these attributes contain malicious content. Use textContent or setAttribute() instead to safely set these values on a created element.
| modal.innerHTML = ` | |
| <div class="modal-backdrop"></div> | |
| <div class="modal-content"> | |
| <button class="modal-close" aria-label="Close">×</button> | |
| <img src="${img.src}" alt="${img.alt}"> | |
| </div> | |
| `; | |
| // Backdrop | |
| const backdrop = document.createElement('div'); | |
| backdrop.className = 'modal-backdrop'; | |
| // Content container | |
| const content = document.createElement('div'); | |
| content.className = 'modal-content'; | |
| // Close button | |
| const closeButton = document.createElement('button'); | |
| closeButton.className = 'modal-close'; | |
| closeButton.setAttribute('aria-label', 'Close'); | |
| closeButton.textContent = '\u00d7'; | |
| // Image element with safely set attributes | |
| const zoomedImg = document.createElement('img'); | |
| // Use currentSrc if available to reflect the actually displayed resource | |
| zoomedImg.src = img.currentSrc || img.src || ''; | |
| zoomedImg.alt = img.alt || ''; | |
| // Assemble modal content | |
| content.appendChild(closeButton); | |
| content.appendChild(zoomedImg); | |
| modal.appendChild(backdrop); | |
| modal.appendChild(content); |
| # Generate HTML based on multimedia type | ||
| if mm_type in ["video", "animation"]: | ||
| return f'''<div class="media-embed-container"> | ||
| <div data-s1000d-multimedia="video" data-multimedia-path="{mm_path}" data-multimedia-code="{safe(mm_code)}"> |
There was a problem hiding this comment.
The mm_path variable is not escaped before being inserted into the HTML attribute. While mm_code uses the safe() function, mm_path should also be escaped to prevent potential attribute injection attacks. Apply safe() or equivalent HTML attribute escaping to mm_path.
| </div>''' | ||
| elif mm_type == "audio": | ||
| return f'''<div class="media-embed-container"> | ||
| <div data-s1000d-multimedia="audio" data-multimedia-path="{mm_path}" data-multimedia-code="{safe(mm_code)}"> |
There was a problem hiding this comment.
The mm_path variable is not escaped before being inserted into the HTML attribute. Apply safe() or equivalent HTML attribute escaping to mm_path to prevent attribute injection.
| <div data-s1000d-multimedia="image" data-multimedia-path="{mm_path}" data-multimedia-code="{safe(mm_code)}"> | ||
| <img src="{mm_path}" alt="{safe(mm_code)}" data-interactive="zoom"> |
There was a problem hiding this comment.
The mm_path variable is used in both HTML attributes (data-multimedia-path and src) without escaping. Apply safe() to mm_path in both locations to prevent attribute injection and XSS vulnerabilities.
| return f'''<div class="media-embed-container"> | ||
| <div data-s1000d-multimedia="video" data-multimedia-path="{mm_path}" data-multimedia-code="{safe(mm_code)}"> | ||
| <video controls> | ||
| <source src="{mm_path}" type="video/mp4"> |
There was a problem hiding this comment.
The mm_path variable should be escaped with safe() before being inserted into the src attribute to prevent potential attribute injection attacks.
| return f'''<div class="media-embed-container"> | ||
| <div data-s1000d-multimedia="audio" data-multimedia-path="{mm_path}" data-multimedia-code="{safe(mm_code)}"> | ||
| <audio controls> | ||
| <source src="{mm_path}" type="audio/mpeg"> |
There was a problem hiding this comment.
The mm_path variable should be escaped with safe() before being inserted into the src attribute to prevent potential attribute injection attacks.
| container.innerHTML = ''; | ||
| container.appendChild(img); | ||
|
|
||
| this.enhanceImage(img); |
There was a problem hiding this comment.
When embedImage() is called, it sets data-interactive='zoom' attribute on images via the HTML generation in build_ietp.py, but enhanceImage() is called without that attribute being explicitly set. The enhanceImage() method checks for img.dataset.interactive === 'zoom' at line 134, which will work from the HTML but not when programmatically created. Consider explicitly setting the data-interactive attribute on the img element before calling enhanceImage if zoom functionality is desired for all embedded images.
|
|
||
| // Calculate aspect ratio padding | ||
| if (type === 'video' || type === 'image') { | ||
| const [width, height] = this.options.aspectRatio.split(':').map(Number); |
There was a problem hiding this comment.
If this.options.aspectRatio is not in the expected 'width:height' format (e.g., malformed or missing), the destructuring will fail and width or height could be undefined or NaN, causing incorrect padding calculation. Add validation to ensure the aspect ratio format is valid before processing.
| const [width, height] = this.options.aspectRatio.split(':').map(Number); | |
| // Validate aspectRatio format ('width:height') and fall back to 16:9 if invalid | |
| const rawRatio = this.options && typeof this.options.aspectRatio === 'string' | |
| ? this.options.aspectRatio | |
| : ''; | |
| let width; | |
| let height; | |
| if (rawRatio) { | |
| const parts = rawRatio.split(':'); | |
| if (parts.length === 2) { | |
| width = Number(parts[0]); | |
| height = Number(parts[1]); | |
| } | |
| } | |
| // Fallback to default 16:9 if parsing failed or produced invalid values | |
| if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) { | |
| width = 16; | |
| height = 9; | |
| } |
| const escHandler = (e) => { | ||
| if (e.key === 'Escape') { | ||
| close(); | ||
| document.removeEventListener('keydown', escHandler); | ||
| } | ||
| }; | ||
| document.addEventListener('keydown', escHandler); |
There was a problem hiding this comment.
The ESC key handler is only removed when the ESC key is pressed. If the modal is closed via the close button or backdrop click, the event listener remains attached, creating a memory leak. Move the removeEventListener call into the close() function to ensure it's always cleaned up.
Implements interactive multimedia embedding for the IETP system, enabling S1000D data modules to render video, audio, and images with responsive controls and accessibility features.
Implementation
MediaEmbed Component (
assets/js/media-embed.js, 359 lines)<video>,<audio>,<img>elements with interactive controls<multimedia>references viadata-s1000d-multimediaattributesautoplay,controls,loop,aspectRatioResponsive Styling (
assets/css/media-embed.css, 411 lines)prefers-reduced-motion, high contrast modeBuild System Integration (
build_ietp.py)render_multimedia_object()converts S1000D<multimedia>elements to HTML<multimediaCaption>Usage
S1000D XML:
Generated HTML:
Screenshots
IETP Index with multimedia support:

Multimedia demo page showing video, audio, interactive images:

File Structure
Technical Notes
Original prompt
IETP typically involves embedding interactive and dynamic content into the UI, such as:
• Dynamic linking between content sections or external resources.
• Embedding multimedia (audio, video, diagrams, etc.).
• Responsive and enriched user interactions like popups, tooltips, and collapsible sections.
To implement and experiment with IETP features in the repository, we need:
• Embedding content dynamically.
• Providing navigation or interaction control for better accessibility and user experience.
Here’s how you can approach experimenting with IETP features systematically:
a. Embedding Multimedia Content
• For embedding videos/audio directly in the UI, create a dedicated component to render these elements.
• Implement parameterized autoplay (deterministic behavior), user controls, and responsive scaling.
Suggested Steps:
• Build or enhance a React MediaEmbed component if the repository uses React.
• Ensure compatibility with Markdown for descriptions, e.g., Markdown rendering embedded components using HTML-like syntax
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.