✨ comic embed
This commit is contained in:
129
static/js/embed.js
Normal file
129
static/js/embed.js
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
// Embed functionality for Sunday Comics
|
||||||
|
// Handles showing embed code modal and copying to clipboard
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const modal = document.getElementById('embed-modal');
|
||||||
|
const embedButton = document.getElementById('embed-button');
|
||||||
|
const closeButton = modal ? modal.querySelector('.modal-close') : null;
|
||||||
|
const embedCodeTextarea = document.getElementById('embed-code');
|
||||||
|
const copyButton = document.getElementById('copy-embed-code');
|
||||||
|
const previewLink = document.getElementById('embed-preview-link');
|
||||||
|
|
||||||
|
if (!modal || !embedButton) {
|
||||||
|
// Embed feature not enabled or elements not found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the site URL from the page (we'll add it as a data attribute)
|
||||||
|
const siteUrl = document.body.getAttribute('data-site-url') || window.location.origin;
|
||||||
|
|
||||||
|
// Open modal when embed button is clicked
|
||||||
|
embedButton.addEventListener('click', function() {
|
||||||
|
const comicNumber = this.getAttribute('data-comic-number');
|
||||||
|
if (!comicNumber) return;
|
||||||
|
|
||||||
|
// Generate embed code
|
||||||
|
const embedUrl = `${siteUrl}/embed/${comicNumber}`;
|
||||||
|
const embedCode = `<iframe src="${embedUrl}" width="800" height="600" frameborder="0" scrolling="no" style="max-width: 100%;" title="Comic #${comicNumber}"></iframe>`;
|
||||||
|
|
||||||
|
// Set the embed code in the textarea
|
||||||
|
embedCodeTextarea.value = embedCode;
|
||||||
|
|
||||||
|
// Set the preview link
|
||||||
|
previewLink.href = embedUrl;
|
||||||
|
|
||||||
|
// Show the modal
|
||||||
|
modal.style.display = 'block';
|
||||||
|
modal.setAttribute('aria-hidden', 'false');
|
||||||
|
|
||||||
|
// Focus on the textarea
|
||||||
|
embedCodeTextarea.focus();
|
||||||
|
embedCodeTextarea.select();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close modal when close button is clicked
|
||||||
|
if (closeButton) {
|
||||||
|
closeButton.addEventListener('click', function() {
|
||||||
|
closeModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close modal when clicking outside the modal content
|
||||||
|
modal.addEventListener('click', function(event) {
|
||||||
|
if (event.target === modal) {
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close modal with Escape key
|
||||||
|
document.addEventListener('keydown', function(event) {
|
||||||
|
if (event.key === 'Escape' && modal.style.display === 'block') {
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copy embed code to clipboard
|
||||||
|
if (copyButton) {
|
||||||
|
copyButton.addEventListener('click', function() {
|
||||||
|
embedCodeTextarea.select();
|
||||||
|
embedCodeTextarea.setSelectionRange(0, 99999); // For mobile devices
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Modern clipboard API
|
||||||
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
navigator.clipboard.writeText(embedCodeTextarea.value).then(function() {
|
||||||
|
showCopyFeedback();
|
||||||
|
}).catch(function() {
|
||||||
|
// Fallback to execCommand
|
||||||
|
fallbackCopy();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Fallback for older browsers
|
||||||
|
fallbackCopy();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
fallbackCopy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fallbackCopy() {
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
showCopyFeedback();
|
||||||
|
} catch (err) {
|
||||||
|
alert('Failed to copy. Please select and copy manually.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showCopyFeedback() {
|
||||||
|
const originalText = copyButton.textContent;
|
||||||
|
copyButton.textContent = 'Copied!';
|
||||||
|
copyButton.classList.add('copied');
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
copyButton.textContent = originalText;
|
||||||
|
copyButton.classList.remove('copied');
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
modal.setAttribute('aria-hidden', 'true');
|
||||||
|
|
||||||
|
// Return focus to the embed button
|
||||||
|
if (embedButton) {
|
||||||
|
embedButton.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update embed button when comic changes via client-side navigation
|
||||||
|
// This integrates with the existing comic-nav.js functionality
|
||||||
|
window.addEventListener('comicUpdated', function(event) {
|
||||||
|
if (event.detail && event.detail.comicNumber && embedButton) {
|
||||||
|
embedButton.setAttribute('data-comic-number', event.detail.comicNumber);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
156
templates/embed.html
Normal file
156
templates/embed.html
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{ title }} - {{ comic_name }}</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.embed-container {
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
border: 2px solid #000;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.embed-header {
|
||||||
|
padding: 15px;
|
||||||
|
border-bottom: 2px solid #000;
|
||||||
|
background: #fff;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
.embed-header-text {
|
||||||
|
flex: 1;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.embed-logo {
|
||||||
|
max-width: 120px;
|
||||||
|
max-height: 60px;
|
||||||
|
height: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.embed-title {
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0 0 5px 0;
|
||||||
|
color: #000;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
.embed-date {
|
||||||
|
font-size: 0.75em;
|
||||||
|
color: #666;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.embed-image-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: 2px solid #000;
|
||||||
|
}
|
||||||
|
.embed-image-link {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
.embed-image {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.embed-footer {
|
||||||
|
padding: 12px 15px;
|
||||||
|
font-size: 0.75em;
|
||||||
|
background: #f8f8f8;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
.embed-footer a {
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
.embed-footer a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.embed-alt-text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
/* Mobile responsive */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.embed-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.embed-logo {
|
||||||
|
max-width: 100px;
|
||||||
|
max-height: 50px;
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
.embed-title {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
.embed-date {
|
||||||
|
font-size: 0.7em;
|
||||||
|
}
|
||||||
|
.embed-footer {
|
||||||
|
font-size: 0.7em;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="embed-container">
|
||||||
|
<div class="embed-header">
|
||||||
|
<div class="embed-header-text">
|
||||||
|
<h1 class="embed-title">{{ comic.title if comic.title else '#' ~ comic.number }}</h1>
|
||||||
|
<p class="embed-date">{{ comic.formatted_date }}</p>
|
||||||
|
</div>
|
||||||
|
{% if logo_image %}
|
||||||
|
<img src="{{ site_url }}/static/images/{{ logo_image }}" alt="{{ comic_name }}" class="embed-logo">
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="embed-image-wrapper">
|
||||||
|
<a href="{{ site_url }}/comic/{{ comic.number }}" class="embed-image-link" target="_blank" rel="noopener noreferrer" aria-label="View {{ comic.title if comic.title else 'comic #' ~ comic.number }} on {{ comic_name }}">
|
||||||
|
{% if comic.mobile_filename %}
|
||||||
|
<picture>
|
||||||
|
<source media="(max-width: 768px)" srcset="{{ url_for('static', filename='images/comics/' + comic.mobile_filename) }}">
|
||||||
|
<img src="{{ url_for('static', filename='images/comics/' + comic.filename) }}"
|
||||||
|
alt="{{ comic.title if comic.title else '#' ~ comic.number }}"
|
||||||
|
title="{{ comic.alt_text }}"
|
||||||
|
class="embed-image">
|
||||||
|
</picture>
|
||||||
|
{% else %}
|
||||||
|
<img src="{{ url_for('static', filename='images/comics/' + comic.filename) }}"
|
||||||
|
alt="{{ comic.title if comic.title else '#' ~ comic.number }}"
|
||||||
|
title="{{ comic.alt_text }}"
|
||||||
|
class="embed-image">
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="embed-footer">
|
||||||
|
<a href="{{ site_url }}/comic/{{ comic.number }}" target="_blank" rel="noopener noreferrer">View on {{ comic_name }} →</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user