- Embed
This commit is contained in:
@@ -0,0 +1,246 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* embed.php — Standalone embeddable player page.
|
||||||
|
* Designed to be used inside an <iframe> on third-party sites.
|
||||||
|
*
|
||||||
|
* Usage: <iframe src="https://yoursite.com/embed.php?v=video-slug"></iframe>
|
||||||
|
*/
|
||||||
|
require_once __DIR__ . '/includes/db.php';
|
||||||
|
|
||||||
|
$slug = trim($_GET['v'] ?? '');
|
||||||
|
$video = $slug ? get_video_by_slug($slug) : null;
|
||||||
|
|
||||||
|
// Determine absolute base URL for media files.
|
||||||
|
// We need absolute URLs here because the embed lives on a foreign domain.
|
||||||
|
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
||||||
|
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
|
||||||
|
$script = dirname($_SERVER['SCRIPT_NAME'] ?? '/');
|
||||||
|
$script = rtrim($script, '/');
|
||||||
|
$base_url = $scheme . '://' . $host . $script . '/'; // e.g. https://tyclifford.com/live/
|
||||||
|
|
||||||
|
// X-Frame-Options: allow embedding from any origin.
|
||||||
|
// If you want to restrict, change ALLOWALL to SAMEORIGIN or remove for CSP header instead.
|
||||||
|
header('X-Frame-Options: ALLOWALL');
|
||||||
|
header('Content-Security-Policy: frame-ancestors *');
|
||||||
|
?>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><?= $video ? htmlspecialchars($video['title'], ENT_QUOTES) . ' — TyClifford.com' : 'Video Player' ?></title>
|
||||||
|
<link href="https://vjs.zencdn.net/8.10.0/video-js.css" rel="stylesheet"/>
|
||||||
|
<script src="https://vjs.zencdn.net/8.10.0/video.min.js"></script>
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--green: #00e87a;
|
||||||
|
--purple: #b060ff;
|
||||||
|
--bg: #09090f;
|
||||||
|
--text: #e8e8f8;
|
||||||
|
--text-2: #9898c0;
|
||||||
|
--text-3: #58587a;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
width: 100%; height: 100%;
|
||||||
|
background: var(--bg);
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: 'Inter', system-ui, sans-serif;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #000;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top shimmer bar — matches site theme */
|
||||||
|
.shimmer-bar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; right: 0;
|
||||||
|
height: 2px;
|
||||||
|
z-index: 10;
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
#ff424d, #b060ff, #00c8ff, #00e87a,
|
||||||
|
#00c8ff, #b060ff, #ff424d);
|
||||||
|
background-size: 300% 100%;
|
||||||
|
animation: shimmer 5s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes shimmer {
|
||||||
|
from { background-position: 0% 50%; }
|
||||||
|
to { background-position: 200% 50%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Video.js fills the whole container */
|
||||||
|
.video-js {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
position: absolute !important;
|
||||||
|
inset: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom VJS skin to match site */
|
||||||
|
.video-js .vjs-big-play-button {
|
||||||
|
background: rgba(176,96,255,.8) !important;
|
||||||
|
border: 2px solid rgba(176,96,255,.95) !important;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
width: 64px !important; height: 64px !important;
|
||||||
|
top: 50% !important; left: 50% !important;
|
||||||
|
transform: translate(-50%, -50%) !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
line-height: 64px !important;
|
||||||
|
transition: background .2s !important;
|
||||||
|
}
|
||||||
|
.video-js:hover .vjs-big-play-button {
|
||||||
|
background: rgba(176,96,255,1) !important;
|
||||||
|
}
|
||||||
|
.video-js .vjs-control-bar {
|
||||||
|
background: linear-gradient(transparent, rgba(0,0,0,.9)) !important;
|
||||||
|
height: 3.2em !important;
|
||||||
|
}
|
||||||
|
.video-js .vjs-play-progress { background: var(--green) !important; }
|
||||||
|
.video-js .vjs-volume-level { background: var(--green) !important; }
|
||||||
|
.video-js .vjs-slider { background: rgba(255,255,255,.15) !important; }
|
||||||
|
.video-js .vjs-load-progress { background: rgba(255,255,255,.1) !important; }
|
||||||
|
.video-js .vjs-current-time,
|
||||||
|
.video-js .vjs-duration,
|
||||||
|
.video-js .vjs-time-divider { color: rgba(255,255,255,.8) !important; font-size: .7em !important; }
|
||||||
|
|
||||||
|
/* Title + branding overlay at top (fades out) */
|
||||||
|
.title-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px; left: 12px; right: 12px;
|
||||||
|
z-index: 5;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity .5s;
|
||||||
|
}
|
||||||
|
.title-overlay.hidden { opacity: 0; }
|
||||||
|
|
||||||
|
.title-text {
|
||||||
|
font-size: .78rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 1px 6px rgba(0,0,0,.8);
|
||||||
|
line-height: 1.3;
|
||||||
|
max-width: 70%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.brand-link {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: .6rem;
|
||||||
|
letter-spacing: .12em;
|
||||||
|
color: rgba(255,255,255,.55);
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: 0 1px 4px rgba(0,0,0,.8);
|
||||||
|
pointer-events: all;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: color .2s;
|
||||||
|
}
|
||||||
|
.brand-link:hover { color: var(--green); }
|
||||||
|
|
||||||
|
/* No-video state */
|
||||||
|
.no-video {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%; height: 100%;
|
||||||
|
gap: .75rem;
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
.no-video p {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: .72rem;
|
||||||
|
color: var(--text-3);
|
||||||
|
letter-spacing: .1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="player-wrap" id="player-wrap">
|
||||||
|
<div class="shimmer-bar"></div>
|
||||||
|
|
||||||
|
<?php if ($video && !empty($video['sources'])): ?>
|
||||||
|
|
||||||
|
<!-- Title + brand overlay -->
|
||||||
|
<div class="title-overlay" id="title-overlay">
|
||||||
|
<span class="title-text"><?= htmlspecialchars($video['title'], ENT_QUOTES) ?></span>
|
||||||
|
<a class="brand-link" href="<?= $base_url ?>index.php?v=<?= urlencode($video['slug']) ?>"
|
||||||
|
target="_blank" rel="noopener">tyclifford.com</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<video id="embed-player"
|
||||||
|
class="video-js vjs-default-skin vjs-big-play-centered"
|
||||||
|
controls preload="metadata"
|
||||||
|
<?php if ($video['thumbnail']): ?>
|
||||||
|
poster="<?= htmlspecialchars($base_url . MEDIA_URL . $video['thumbnail'], ENT_QUOTES) ?>"
|
||||||
|
<?php endif; ?>>
|
||||||
|
<?php foreach ($video['sources'] as $src): ?>
|
||||||
|
<source
|
||||||
|
src="<?= htmlspecialchars($base_url . MEDIA_URL . $src['file_path'], ENT_QUOTES) ?>"
|
||||||
|
type="<?= htmlspecialchars($src['mime_type'], ENT_QUOTES) ?>">
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<p class="vjs-no-js">Your browser does not support HTML5 video.</p>
|
||||||
|
</video>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<div class="no-video">
|
||||||
|
<svg width="40" height="40" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="#58587a" stroke-width="1.5">
|
||||||
|
<rect x="2" y="3" width="20" height="14" rx="2"/>
|
||||||
|
<line x1="8" y1="21" x2="16" y2="21"/>
|
||||||
|
<line x1="12" y1="17" x2="12" y2="21"/>
|
||||||
|
</svg>
|
||||||
|
<p><?= $slug ? 'video not found' : 'no video specified' ?></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($video && !empty($video['sources'])): ?>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
var player = videojs('embed-player', {
|
||||||
|
controls: true,
|
||||||
|
autoplay: false,
|
||||||
|
preload: 'metadata',
|
||||||
|
fluid: false,
|
||||||
|
responsive: false,
|
||||||
|
playbackRates: [0.5, 1, 1.25, 1.5, 2],
|
||||||
|
controlBar: {
|
||||||
|
children: [
|
||||||
|
'playToggle', 'volumePanel', 'currentTimeDisplay',
|
||||||
|
'timeDivider', 'durationDisplay', 'progressControl',
|
||||||
|
'playbackRateMenuButton', 'fullscreenToggle'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hide title overlay once user starts playing
|
||||||
|
var overlay = document.getElementById('title-overlay');
|
||||||
|
player.on('play', function () {
|
||||||
|
if (overlay) overlay.classList.add('hidden');
|
||||||
|
});
|
||||||
|
player.on('pause', function () {
|
||||||
|
if (overlay) overlay.classList.remove('hidden');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -219,7 +219,7 @@ function render_head(string $title = 'Media — TyClifford.com', string $extra_c
|
|||||||
|
|
||||||
function render_topbar(string $active = ''): void {
|
function render_topbar(string $active = ''): void {
|
||||||
$title = setting('site_title', 'Ty Clifford');
|
$title = setting('site_title', 'Ty Clifford');
|
||||||
$sub = setting('site_sub', 'tyclifford.com / media');
|
$sub = setting('site_sub', 'tyclifford.com / player');
|
||||||
?>
|
?>
|
||||||
<header class="topbar">
|
<header class="topbar">
|
||||||
<div class="topbar-brand">
|
<div class="topbar-brand">
|
||||||
@@ -243,7 +243,7 @@ function render_topbar(string $active = ''): void {
|
|||||||
|
|
||||||
function render_footer(): void { ?>
|
function render_footer(): void { ?>
|
||||||
<footer class="page-footer" role="contentinfo">
|
<footer class="page-footer" role="contentinfo">
|
||||||
<p class="footer-note">TyClifford.com · media</p>
|
<p class="footer-note">TyClifford.com · player</p>
|
||||||
<p class="footer-note"><a href="https://git.tyclifford.com/">open source</a></p>
|
<p class="footer-note"><a href="https://git.tyclifford.com/">open source</a></p>
|
||||||
<p class="footer-note">hobbies are my hobby</p>
|
<p class="footer-note">hobbies are my hobby</p>
|
||||||
<p class="footer-note"><a href="mailto:ty@tyclifford.com">ty@tyclifford.com</a></p>
|
<p class="footer-note"><a href="mailto:ty@tyclifford.com">ty@tyclifford.com</a></p>
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ $per_page = (int)setting('catalogue_per_page', '10');
|
|||||||
$page = max(1, (int)($_GET['page'] ?? 1));
|
$page = max(1, (int)($_GET['page'] ?? 1));
|
||||||
$paged = get_videos_paginated($page, $per_page);
|
$paged = get_videos_paginated($page, $per_page);
|
||||||
|
|
||||||
|
// Build absolute base URL used for embed snippet generation
|
||||||
|
$_scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
||||||
|
$_host = $_SERVER['HTTP_HOST'] ?? 'localhost';
|
||||||
|
$_script = rtrim(dirname($_SERVER['SCRIPT_NAME'] ?? '/'), '/');
|
||||||
|
$_base_url = $_scheme . '://' . $_host . $_script . '/';
|
||||||
|
|
||||||
render_head('Media — TyClifford.com', '
|
render_head('Media — TyClifford.com', '
|
||||||
/* ── Catalogue grid ── */
|
/* ── Catalogue grid ── */
|
||||||
.cat-section { display:flex; flex-direction:column; gap:1rem; }
|
.cat-section { display:flex; flex-direction:column; gap:1rem; }
|
||||||
@@ -141,6 +147,61 @@ render_head('Media — TyClifford.com', '
|
|||||||
/* pagination row */
|
/* pagination row */
|
||||||
.cat-footer { display:flex; align-items:center; justify-content:space-between; gap:1rem; flex-wrap:wrap; }
|
.cat-footer { display:flex; align-items:center; justify-content:space-between; gap:1rem; flex-wrap:wrap; }
|
||||||
.cat-count { font-family:"Share Tech Mono",monospace; font-size:.65rem; color:var(--text-3); letter-spacing:.06em; }
|
.cat-count { font-family:"Share Tech Mono",monospace; font-size:.65rem; color:var(--text-3); letter-spacing:.06em; }
|
||||||
|
|
||||||
|
/* ── Embed panel ── */
|
||||||
|
.embed-panel {
|
||||||
|
border-top:1px solid var(--border);
|
||||||
|
overflow:hidden;
|
||||||
|
max-height:0;
|
||||||
|
transition:max-height .35s cubic-bezier(.4,0,.2,1), padding .35s;
|
||||||
|
padding:0 1.1rem;
|
||||||
|
}
|
||||||
|
.embed-panel.open { max-height:320px; padding:.9rem 1.1rem; }
|
||||||
|
.embed-panel-inner { display:flex; flex-direction:column; gap:.75rem; }
|
||||||
|
.embed-label { font-family:"Share Tech Mono",monospace; font-size:.6rem; letter-spacing:.14em; text-transform:uppercase; color:var(--text-3); }
|
||||||
|
.embed-code-wrap { position:relative; }
|
||||||
|
.embed-code {
|
||||||
|
display:block; width:100%;
|
||||||
|
background:var(--surface-2); border:1px solid var(--border-2); border-radius:var(--r);
|
||||||
|
padding:.65rem .85rem; padding-right:5.5rem;
|
||||||
|
font-family:"Share Tech Mono",monospace; font-size:.68rem; color:var(--text-2);
|
||||||
|
white-space:nowrap; overflow-x:auto; line-height:1.6;
|
||||||
|
resize:none; cursor:text;
|
||||||
|
scrollbar-width:thin;
|
||||||
|
}
|
||||||
|
.embed-copy-btn {
|
||||||
|
position:absolute; right:.4rem; top:50%; transform:translateY(-50%);
|
||||||
|
padding:.32rem .7rem; font-size:.65rem; font-weight:600;
|
||||||
|
background:rgba(176,96,255,.15); color:var(--purple);
|
||||||
|
border:1px solid rgba(176,96,255,.35); border-radius:calc(var(--r) - 1px);
|
||||||
|
cursor:pointer; font-family:"Share Tech Mono",monospace; letter-spacing:.06em;
|
||||||
|
transition:background .2s, color .2s;
|
||||||
|
white-space:nowrap;
|
||||||
|
}
|
||||||
|
.embed-copy-btn:hover { background:rgba(176,96,255,.28); }
|
||||||
|
.embed-copy-btn.copied { background:rgba(0,232,122,.15); color:var(--green); border-color:rgba(0,232,122,.35); }
|
||||||
|
.embed-row { display:flex; gap:.6rem; align-items:center; flex-wrap:wrap; }
|
||||||
|
.embed-direct-link {
|
||||||
|
font-family:"Share Tech Mono",monospace; font-size:.65rem; color:var(--text-3);
|
||||||
|
text-decoration:none; border-bottom:1px solid rgba(255,255,255,.1);
|
||||||
|
transition:color .15s, border-color .15s;
|
||||||
|
}
|
||||||
|
.embed-direct-link:hover { color:var(--green); border-color:rgba(0,232,122,.4); }
|
||||||
|
.embed-preview-link {
|
||||||
|
font-family:"Share Tech Mono",monospace; font-size:.65rem; color:var(--purple);
|
||||||
|
text-decoration:none; border-bottom:1px solid rgba(176,96,255,.2);
|
||||||
|
transition:color .15s;
|
||||||
|
}
|
||||||
|
.embed-preview-link:hover { color:#c880ff; }
|
||||||
|
.embed-toggle-btn {
|
||||||
|
display:inline-flex; align-items:center; gap:.4rem;
|
||||||
|
font-family:"Share Tech Mono",monospace; font-size:.65rem; letter-spacing:.08em;
|
||||||
|
color:var(--text-3); background:transparent; border:1px solid var(--border-2);
|
||||||
|
border-radius:var(--r); padding:.35rem .7rem; cursor:pointer;
|
||||||
|
transition:color .2s, border-color .2s, background .2s;
|
||||||
|
}
|
||||||
|
.embed-toggle-btn:hover { color:var(--text-2); border-color:var(--purple); background:rgba(176,96,255,.07); }
|
||||||
|
.embed-toggle-btn.active { color:var(--purple); border-color:var(--purple); background:rgba(176,96,255,.1); }
|
||||||
');
|
');
|
||||||
?>
|
?>
|
||||||
<main class="page" role="main">
|
<main class="page" role="main">
|
||||||
@@ -181,7 +242,44 @@ render_head('Media — TyClifford.com', '
|
|||||||
<span><?= format_duration((int)$video['duration']) ?></span>
|
<span><?= format_duration((int)$video['duration']) ?></span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</p>
|
</p>
|
||||||
|
<?php if ($video): ?>
|
||||||
|
<button class="embed-toggle-btn" id="embed-toggle" aria-expanded="false" aria-controls="embed-panel">
|
||||||
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
|
||||||
|
Embed
|
||||||
|
</button>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?php if ($video):
|
||||||
|
$embed_url = $_base_url . 'embed.php?v=' . urlencode($video['slug']);
|
||||||
|
$iframe_code = '<iframe src="' . $embed_url . '" width="560" height="315" frameborder="0" allowfullscreen allow="fullscreen"></iframe>';
|
||||||
|
?>
|
||||||
|
<div class="embed-panel" id="embed-panel" role="region" aria-label="Embed code">
|
||||||
|
<div class="embed-panel-inner">
|
||||||
|
<div class="form-group" style="gap:.35rem;">
|
||||||
|
<span class="embed-label">iframe embed code</span>
|
||||||
|
<div class="embed-code-wrap">
|
||||||
|
<code class="embed-code" id="embed-code-iframe"><?= h($iframe_code) ?></code>
|
||||||
|
<button class="embed-copy-btn" data-target="embed-code-iframe" onclick="copyEmbed(this)">Copy</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="embed-row">
|
||||||
|
<span class="embed-label">Direct player URL —</span>
|
||||||
|
<a class="embed-direct-link" href="<?= h($embed_url) ?>" target="_blank" rel="noopener">
|
||||||
|
<?= h($embed_url) ?>
|
||||||
|
</a>
|
||||||
|
<a class="embed-preview-link" href="<?= h($embed_url) ?>" target="_blank" rel="noopener">
|
||||||
|
↗ Preview
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="embed-label" style="color:var(--text-3);font-size:.58rem;">
|
||||||
|
Recommended: width="560" height="315" — any 16:9 size works. Add style="border:0;border-radius:6px" for no border.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ═══════════════════════════ VIDEO CATALOGUE ═══════════════════════════ -->
|
<!-- ═══════════════════════════ VIDEO CATALOGUE ═══════════════════════════ -->
|
||||||
@@ -305,7 +403,50 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Embed panel toggle
|
||||||
|
var toggleBtn = document.getElementById('embed-toggle');
|
||||||
|
var panel = document.getElementById('embed-panel');
|
||||||
|
if (toggleBtn && panel) {
|
||||||
|
toggleBtn.addEventListener('click', function() {
|
||||||
|
var open = panel.classList.toggle('open');
|
||||||
|
toggleBtn.classList.toggle('active', open);
|
||||||
|
toggleBtn.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Copy embed code to clipboard
|
||||||
|
function copyEmbed(btn) {
|
||||||
|
var targetId = btn.getAttribute('data-target');
|
||||||
|
var el = document.getElementById(targetId);
|
||||||
|
if (!el) return;
|
||||||
|
var text = el.textContent || el.innerText;
|
||||||
|
navigator.clipboard.writeText(text).then(function() {
|
||||||
|
btn.textContent = 'Copied!';
|
||||||
|
btn.classList.add('copied');
|
||||||
|
setTimeout(function() {
|
||||||
|
btn.textContent = 'Copy';
|
||||||
|
btn.classList.remove('copied');
|
||||||
|
}, 2000);
|
||||||
|
}).catch(function() {
|
||||||
|
// Fallback for older browsers
|
||||||
|
var ta = document.createElement('textarea');
|
||||||
|
ta.value = text;
|
||||||
|
ta.style.position = 'fixed';
|
||||||
|
ta.style.opacity = '0';
|
||||||
|
document.body.appendChild(ta);
|
||||||
|
ta.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(ta);
|
||||||
|
btn.textContent = 'Copied!';
|
||||||
|
btn.classList.add('copied');
|
||||||
|
setTimeout(function() {
|
||||||
|
btn.textContent = 'Copy';
|
||||||
|
btn.classList.remove('copied');
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user