794 lines
27 KiB
JavaScript
794 lines
27 KiB
JavaScript
/**
|
||
* Shortcodes JavaScript - 实况照片、语音、音乐卡片
|
||
* 移植自 Hugo-Theme-Amigo
|
||
*/
|
||
|
||
// ============================================================
|
||
// Global Audio Manager - 全局音频/视频管理器
|
||
// 确保所有音频控件互斥播放,PJAX 切换时保存进度
|
||
// ============================================================
|
||
|
||
var AudioManager = {
|
||
currentPlaying: null,
|
||
currentType: null,
|
||
savedStates: {},
|
||
// 全局音乐播放器 - PJAX 持久化
|
||
globalAudio: null,
|
||
globalMusicSrc: null,
|
||
globalMusicCard: null,
|
||
// Session storage key
|
||
STORAGE_KEY: 'amigo_music_state',
|
||
|
||
// 获取当前页面的存储 key(基于 URL path)
|
||
_getPageKey: function() {
|
||
return window.location.pathname;
|
||
},
|
||
|
||
init: function() {
|
||
// 创建全局音频元素
|
||
if (!this.globalAudio) {
|
||
this.globalAudio = new Audio();
|
||
this.globalAudio.preload = 'metadata';
|
||
|
||
// 监听全局音频事件
|
||
this.globalAudio.addEventListener('timeupdate', this._onTimeUpdate.bind(this));
|
||
this.globalAudio.addEventListener('ended', this._onEnded.bind(this));
|
||
this.globalAudio.addEventListener('loadedmetadata', this._onLoadedMetadata.bind(this));
|
||
}
|
||
},
|
||
|
||
// 保存当前播放状态到 sessionStorage(按页面保存)
|
||
saveToSession: function() {
|
||
if (!this.globalAudio || !this.globalMusicSrc) return;
|
||
|
||
var pageKey = this._getPageKey();
|
||
var allStates = this._loadAllStates();
|
||
|
||
allStates[pageKey] = {
|
||
src: this.globalMusicSrc,
|
||
currentTime: this.globalAudio.currentTime,
|
||
isPlaying: !this.globalAudio.paused,
|
||
name: this.globalMusicCard ? this.globalMusicCard.dataset.name : '',
|
||
artist: this.globalMusicCard ? this.globalMusicCard.dataset.artist : ''
|
||
};
|
||
|
||
try {
|
||
sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify(allStates));
|
||
} catch (e) {
|
||
console.warn('Failed to save music state:', e);
|
||
}
|
||
},
|
||
|
||
// 加载所有页面的状态
|
||
_loadAllStates: function() {
|
||
try {
|
||
var stored = sessionStorage.getItem(this.STORAGE_KEY);
|
||
if (!stored) return {};
|
||
return JSON.parse(stored);
|
||
} catch (e) {
|
||
return {};
|
||
}
|
||
},
|
||
|
||
// 从 sessionStorage 恢复当前页面的播放状态
|
||
restoreFromSession: function() {
|
||
var pageKey = this._getPageKey();
|
||
var allStates = this._loadAllStates();
|
||
|
||
var state = allStates[pageKey];
|
||
if (!state || !state.src) return null;
|
||
|
||
return state;
|
||
},
|
||
|
||
// 清除当前页面的播放状态
|
||
clearSession: function() {
|
||
var pageKey = this._getPageKey();
|
||
var allStates = this._loadAllStates();
|
||
|
||
delete allStates[pageKey];
|
||
|
||
try {
|
||
sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify(allStates));
|
||
} catch (e) {}
|
||
},
|
||
|
||
// 恢复播放状态 - 在 PJAX 切换后调用
|
||
restorePlayingState: function() {
|
||
var state = this.restoreFromSession();
|
||
if (!state || !state.src) {
|
||
// 当前页面没有保存的状态,暂停播放
|
||
if (this.globalAudio && !this.globalAudio.paused) {
|
||
this.globalAudio.pause();
|
||
}
|
||
return;
|
||
}
|
||
|
||
var self = this;
|
||
|
||
// 找到对应的音乐卡片
|
||
var cards = document.querySelectorAll('.amigo-music-card');
|
||
var targetCard = null;
|
||
|
||
for (var i = 0; i < cards.length; i++) {
|
||
var card = cards[i];
|
||
if (card.dataset.src === state.src) {
|
||
targetCard = card;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!targetCard) {
|
||
// 当前页面没有这个音乐卡片,暂停播放
|
||
if (this.globalAudio && !this.globalAudio.paused) {
|
||
this.globalAudio.pause();
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 设置全局音频
|
||
this.globalMusicSrc = state.src;
|
||
this.globalMusicCard = targetCard;
|
||
this.globalAudio.src = state.src;
|
||
|
||
// 更新 UI
|
||
this._updateUI(targetCard, state.isPlaying);
|
||
|
||
// 等待元数据加载完成
|
||
this.globalAudio.addEventListener('loadedmetadata', function onLoaded() {
|
||
self.globalAudio.removeEventListener('loadedmetadata', onLoaded);
|
||
|
||
if (state.currentTime > 0 && state.currentTime < self.globalAudio.duration) {
|
||
self.globalAudio.currentTime = state.currentTime;
|
||
}
|
||
|
||
self._onTimeUpdate();
|
||
|
||
if (state.isPlaying) {
|
||
self.globalAudio.play().catch(function() {});
|
||
}
|
||
});
|
||
|
||
if (this.globalAudio.readyState >= 1) {
|
||
if (state.currentTime > 0 && state.currentTime < this.globalAudio.duration) {
|
||
this.globalAudio.currentTime = state.currentTime;
|
||
}
|
||
this._onTimeUpdate();
|
||
if (state.isPlaying) {
|
||
this.globalAudio.play().catch(function() {});
|
||
}
|
||
}
|
||
},
|
||
|
||
// 播放指定音乐卡片
|
||
playMusic: function(card) {
|
||
if (!card) return;
|
||
|
||
var audioSrc = card.dataset.src;
|
||
if (!audioSrc) return;
|
||
|
||
// 如果是同一首歌,切换播放/暂停
|
||
if (this.globalMusicSrc === audioSrc && this.globalAudio) {
|
||
if (this.globalAudio.paused) {
|
||
this.globalAudio.play().catch(function() {});
|
||
this.globalMusicCard = card;
|
||
this._updateUI(card, true);
|
||
} else {
|
||
this.globalAudio.pause();
|
||
this._updateUI(card, false);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 暂停其他所有音频
|
||
this.pauseAll();
|
||
|
||
// 设置新的音频源
|
||
this.globalMusicSrc = audioSrc;
|
||
this.globalMusicCard = card;
|
||
this.globalAudio.src = audioSrc;
|
||
|
||
// 更新 UI
|
||
this._updateUI(card, true);
|
||
|
||
// 尝试播放
|
||
var self = this;
|
||
this.globalAudio.play().then(function() {
|
||
// 保存状态
|
||
self.saveToSession();
|
||
}).catch(function(err) {
|
||
console.warn('Play failed:', err);
|
||
});
|
||
},
|
||
|
||
// 跳转到指定时间
|
||
seekTo: function(time) {
|
||
if (this.globalAudio) {
|
||
this.globalAudio.currentTime = time;
|
||
this.saveToSession();
|
||
}
|
||
},
|
||
|
||
// 更新时间回调
|
||
_onTimeUpdate: function() {
|
||
if (!this.globalAudio || !this.globalMusicCard) return;
|
||
|
||
// 如果正在拖动进度条,跳过 UI 更新
|
||
if (this.globalAudio.__seeking) return;
|
||
|
||
var currentTime = this.globalAudio.currentTime;
|
||
var duration = this.globalAudio.duration;
|
||
|
||
if (isNaN(duration) || duration === 0) return;
|
||
|
||
// 更新进度条
|
||
var progress = (currentTime / duration) * 100;
|
||
this.globalMusicCard.style.setProperty('--music-progress', progress + '%');
|
||
|
||
// 更新时间显示
|
||
var currentTimeEl = this.globalMusicCard.querySelector('.amigo-music-card__time--current');
|
||
if (currentTimeEl) {
|
||
currentTimeEl.textContent = formatTime(currentTime);
|
||
}
|
||
|
||
// 更新总时长(如果还没更新过)
|
||
var durationTimeEl = this.globalMusicCard.querySelector('.amigo-music-card__time--duration');
|
||
if (durationTimeEl && durationTimeEl.textContent === '--:--') {
|
||
durationTimeEl.textContent = formatTime(duration);
|
||
}
|
||
|
||
// 定期保存状态(每5秒)
|
||
if (Math.floor(currentTime) % 5 === 0) {
|
||
this.saveToSession();
|
||
}
|
||
},
|
||
|
||
// 加载完成回调
|
||
_onLoadedMetadata: function() {
|
||
if (!this.globalAudio || !this.globalMusicCard) return;
|
||
|
||
var duration = this.globalAudio.duration;
|
||
|
||
// 更新总时长显示
|
||
var durationTimeEl = this.globalMusicCard.querySelector('.amigo-music-card__time--duration');
|
||
if (durationTimeEl) {
|
||
durationTimeEl.textContent = formatTime(duration);
|
||
}
|
||
},
|
||
|
||
// 播放结束回调
|
||
_onEnded: function() {
|
||
if (this.globalMusicCard) {
|
||
this._updateUI(this.globalMusicCard, false);
|
||
}
|
||
this.globalMusicCard = null;
|
||
this.clearSession();
|
||
},
|
||
|
||
// 更新 UI 状态
|
||
_updateUI: function(card, isPlaying) {
|
||
if (!card) return;
|
||
|
||
card.setAttribute('data-playing', isPlaying ? 'true' : 'false');
|
||
|
||
var playBtn = card.querySelector('.amigo-music-card__play');
|
||
if (playBtn) {
|
||
playBtn.setAttribute('aria-pressed', isPlaying ? 'true' : 'false');
|
||
}
|
||
|
||
if (isPlaying) {
|
||
this.currentPlaying = card;
|
||
this.currentType = 'music';
|
||
} else if (this.globalMusicCard === card) {
|
||
this.currentPlaying = null;
|
||
this.currentType = null;
|
||
}
|
||
},
|
||
|
||
pauseAll: function() {
|
||
// Pause all voice bubbles
|
||
document.querySelectorAll('.amigo-voice-bubble.is-playing').forEach(function(bubble) {
|
||
if (bubble.__audio) {
|
||
try {
|
||
bubble.__audio.pause();
|
||
bubble.__audio.currentTime = 0;
|
||
} catch (e) {}
|
||
}
|
||
bubble.classList.remove('is-playing');
|
||
});
|
||
|
||
// Pause global music
|
||
if (this.globalAudio && !this.globalAudio.paused) {
|
||
this.globalAudio.pause();
|
||
if (this.globalMusicCard) {
|
||
this._updateUI(this.globalMusicCard, false);
|
||
}
|
||
}
|
||
|
||
// Pause all live photos
|
||
document.querySelectorAll('.amigo-livephoto.is-playing').forEach(function(livePhoto) {
|
||
var video = livePhoto.querySelector('.amigo-livephoto-video');
|
||
if (video) {
|
||
try { video.pause(); video.currentTime = 0; } catch (e) {}
|
||
}
|
||
livePhoto.classList.remove('is-playing');
|
||
});
|
||
|
||
this.currentPlaying = null;
|
||
this.currentType = null;
|
||
},
|
||
|
||
registerPlay: function(element, type) {
|
||
if (this.currentPlaying && this.currentPlaying !== element) {
|
||
this.pauseAll();
|
||
}
|
||
this.currentPlaying = element;
|
||
this.currentType = type;
|
||
},
|
||
|
||
saveState: function() {
|
||
var states = [];
|
||
|
||
// Save music card states
|
||
document.querySelectorAll('.amigo-music-card').forEach(function(card, index) {
|
||
var audio = card.__audio;
|
||
if (audio && audio.src) {
|
||
states.push({
|
||
type: 'music',
|
||
src: card.dataset.src,
|
||
currentTime: audio.currentTime,
|
||
isPlaying: card.getAttribute('data-playing') === 'true',
|
||
index: index
|
||
});
|
||
}
|
||
});
|
||
|
||
// Save live photo states
|
||
document.querySelectorAll('.amigo-livephoto').forEach(function(livePhoto, index) {
|
||
var video = livePhoto.querySelector('.amigo-livephoto-video');
|
||
if (video && video.src) {
|
||
states.push({
|
||
type: 'livephoto',
|
||
src: livePhoto.dataset.src,
|
||
currentTime: video.currentTime,
|
||
isPlaying: livePhoto.classList.contains('is-playing'),
|
||
index: index
|
||
});
|
||
}
|
||
});
|
||
|
||
this.savedStates[window.location.pathname] = states;
|
||
},
|
||
|
||
restoreState: function() {
|
||
var path = window.location.pathname;
|
||
var states = this.savedStates[path];
|
||
if (!states || states.length === 0) return;
|
||
|
||
states.forEach(function(state) {
|
||
if (state.type === 'music') {
|
||
var cards = document.querySelectorAll('.amigo-music-card');
|
||
var card = cards[state.index];
|
||
if (card && card.__audio && card.dataset.src === state.src) {
|
||
var audio = card.__audio;
|
||
var playBtn = card.querySelector('.amigo-music-card__play');
|
||
var seekInput = card.querySelector('.amigo-music-card__seek');
|
||
var currentTimeEl = card.querySelector('.amigo-music-card__time--current');
|
||
|
||
audio.addEventListener('loadedmetadata', function onLoaded() {
|
||
audio.currentTime = state.currentTime;
|
||
audio.removeEventListener('loadedmetadata', onLoaded);
|
||
|
||
if (currentTimeEl) {
|
||
currentTimeEl.textContent = formatTime(state.currentTime);
|
||
}
|
||
if (seekInput && audio.duration) {
|
||
var progress = (state.currentTime / audio.duration) * 100;
|
||
seekInput.value = (state.currentTime / audio.duration * 1000).toString();
|
||
card.style.setProperty('--music-progress', progress + '%');
|
||
}
|
||
|
||
if (state.isPlaying) {
|
||
audio.play().catch(function() {});
|
||
card.setAttribute('data-playing', 'true');
|
||
if (playBtn) playBtn.setAttribute('aria-pressed', 'true');
|
||
AudioManager.currentPlaying = card;
|
||
AudioManager.currentType = 'music';
|
||
}
|
||
});
|
||
|
||
if (audio.readyState >= 1) {
|
||
audio.currentTime = state.currentTime;
|
||
if (currentTimeEl) {
|
||
currentTimeEl.textContent = formatTime(state.currentTime);
|
||
}
|
||
if (seekInput && audio.duration) {
|
||
var progress = (state.currentTime / audio.duration) * 100;
|
||
seekInput.value = (state.currentTime / audio.duration * 1000).toString();
|
||
card.style.setProperty('--music-progress', progress + '%');
|
||
}
|
||
if (state.isPlaying) {
|
||
audio.play().catch(function() {});
|
||
card.setAttribute('data-playing', 'true');
|
||
if (playBtn) playBtn.setAttribute('aria-pressed', 'true');
|
||
AudioManager.currentPlaying = card;
|
||
AudioManager.currentType = 'music';
|
||
}
|
||
}
|
||
}
|
||
} else if (state.type === 'livephoto') {
|
||
var photos = document.querySelectorAll('.amigo-livephoto');
|
||
var livePhoto = photos[state.index];
|
||
if (livePhoto && livePhoto.dataset.src === state.src) {
|
||
var video = livePhoto.querySelector('.amigo-livephoto-video');
|
||
if (video) {
|
||
var src = livePhoto.dataset.src || '';
|
||
if (src && !video.getAttribute('src')) {
|
||
video.setAttribute('src', src);
|
||
video.src = src;
|
||
}
|
||
|
||
video.addEventListener('loadedmetadata', function onLoaded() {
|
||
video.currentTime = state.currentTime;
|
||
video.removeEventListener('loadedmetadata', onLoaded);
|
||
|
||
if (state.isPlaying) {
|
||
video.play().catch(function() {});
|
||
livePhoto.classList.add('is-playing');
|
||
AudioManager.currentPlaying = livePhoto;
|
||
AudioManager.currentType = 'livephoto';
|
||
}
|
||
});
|
||
|
||
if (video.readyState >= 1) {
|
||
video.currentTime = state.currentTime;
|
||
if (state.isPlaying) {
|
||
video.play().catch(function() {});
|
||
livePhoto.classList.add('is-playing');
|
||
AudioManager.currentPlaying = livePhoto;
|
||
AudioManager.currentType = 'livephoto';
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
};
|
||
|
||
// ============================================================
|
||
// Live Photo
|
||
// ============================================================
|
||
|
||
function initLivePhotoShortcodes() {
|
||
document.querySelectorAll('.amigo-livephoto').forEach(function(livePhoto) {
|
||
if (livePhoto.__liveBound) return;
|
||
livePhoto.__liveBound = true;
|
||
|
||
var video = livePhoto.querySelector('.amigo-livephoto-video');
|
||
var posterImg = livePhoto.querySelector('.amigo-livephoto-img');
|
||
var soundBtn = livePhoto.querySelector('.amigo-livephoto-sound');
|
||
|
||
if (!video) return;
|
||
|
||
var HOVER_DELAY = 500;
|
||
var hoverTimer = null;
|
||
var isLoaded = false;
|
||
var isUserToggled = false;
|
||
|
||
function ensureLoaded() {
|
||
if (isLoaded) return;
|
||
isLoaded = true;
|
||
var src = video.dataset.src || '';
|
||
if (src && !video.getAttribute('src')) {
|
||
video.setAttribute('src', src);
|
||
video.src = src;
|
||
}
|
||
try { video.load(); } catch (e) {}
|
||
}
|
||
|
||
function setMuted(isMuted) {
|
||
video.muted = !!isMuted;
|
||
if (isMuted) {
|
||
video.setAttribute('muted', '');
|
||
livePhoto.classList.add('is-muted');
|
||
} else {
|
||
video.removeAttribute('muted');
|
||
livePhoto.classList.remove('is-muted');
|
||
}
|
||
}
|
||
|
||
function playVideo() {
|
||
ensureLoaded();
|
||
AudioManager.registerPlay(livePhoto, 'livephoto');
|
||
livePhoto.classList.add('is-playing');
|
||
try { video.play(); } catch (e) {}
|
||
}
|
||
|
||
function stopVideo() {
|
||
livePhoto.classList.remove('is-playing');
|
||
try { video.pause(); video.currentTime = 0; } catch (e) {}
|
||
if (AudioManager.currentPlaying === livePhoto) {
|
||
AudioManager.currentPlaying = null;
|
||
AudioManager.currentType = null;
|
||
}
|
||
}
|
||
|
||
function togglePlay() {
|
||
if (livePhoto.classList.contains('is-playing')) {
|
||
isUserToggled = false;
|
||
stopVideo();
|
||
} else {
|
||
isUserToggled = true;
|
||
playVideo();
|
||
}
|
||
}
|
||
|
||
// Click to toggle
|
||
livePhoto.addEventListener('click', function() {
|
||
togglePlay();
|
||
});
|
||
|
||
// Sound button click
|
||
if (soundBtn) {
|
||
soundBtn.addEventListener('click', function(e) {
|
||
e.stopPropagation();
|
||
setMuted(!video.muted);
|
||
});
|
||
}
|
||
|
||
// Hover to play (desktop)
|
||
if ('ontouchstart' in window === false) {
|
||
livePhoto.addEventListener('mouseenter', function() {
|
||
if (isUserToggled) return;
|
||
hoverTimer = setTimeout(playVideo, HOVER_DELAY);
|
||
});
|
||
|
||
livePhoto.addEventListener('mouseleave', function() {
|
||
clearTimeout(hoverTimer);
|
||
if (!isUserToggled) {
|
||
stopVideo();
|
||
}
|
||
});
|
||
}
|
||
|
||
// Video ended
|
||
video.addEventListener('ended', function() {
|
||
livePhoto.classList.remove('is-playing');
|
||
isUserToggled = false;
|
||
if (AudioManager.currentPlaying === livePhoto) {
|
||
AudioManager.currentPlaying = null;
|
||
AudioManager.currentType = null;
|
||
}
|
||
});
|
||
|
||
// Initialize muted state
|
||
setMuted(livePhoto.classList.contains('is-muted'));
|
||
});
|
||
}
|
||
|
||
// ============================================================
|
||
// Voice Bubble
|
||
// ============================================================
|
||
|
||
function initVoiceBubbles() {
|
||
document.querySelectorAll('.amigo-voice-bubble').forEach(function(bubble) {
|
||
if (bubble.__voiceBound) return;
|
||
bubble.__voiceBound = true;
|
||
|
||
var audioSrc = bubble.dataset.src;
|
||
var audio = null;
|
||
var isPlaying = false;
|
||
var durationSpan = bubble.querySelector('.amigo-voice-duration');
|
||
|
||
// Load audio duration
|
||
if (audioSrc) {
|
||
audio = new Audio(audioSrc);
|
||
bubble.__audio = audio;
|
||
audio.addEventListener('loadedmetadata', function() {
|
||
var duration = Math.round(audio.duration);
|
||
if (durationSpan) {
|
||
durationSpan.textContent = duration + '"';
|
||
}
|
||
});
|
||
}
|
||
|
||
bubble.addEventListener('click', function() {
|
||
if (!audio) return;
|
||
|
||
if (isPlaying) {
|
||
audio.pause();
|
||
audio.currentTime = 0;
|
||
bubble.classList.remove('is-playing');
|
||
isPlaying = false;
|
||
if (AudioManager.currentPlaying === bubble) {
|
||
AudioManager.currentPlaying = null;
|
||
AudioManager.currentType = null;
|
||
}
|
||
} else {
|
||
AudioManager.registerPlay(bubble, 'voice');
|
||
audio.play();
|
||
bubble.classList.add('is-playing');
|
||
isPlaying = true;
|
||
}
|
||
});
|
||
|
||
audio.addEventListener('ended', function() {
|
||
bubble.classList.remove('is-playing');
|
||
isPlaying = false;
|
||
if (AudioManager.currentPlaying === bubble) {
|
||
AudioManager.currentPlaying = null;
|
||
AudioManager.currentType = null;
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// ============================================================
|
||
// Music Card - PJAX 持久化版本
|
||
// 使用全局 AudioManager 管理音乐播放
|
||
// ============================================================
|
||
|
||
function initMusicCards() {
|
||
document.querySelectorAll('.amigo-music-card').forEach(function(card) {
|
||
if (card.__musicBound) return;
|
||
card.__musicBound = true;
|
||
|
||
var audioSrc = card.dataset.src;
|
||
var playBtn = card.querySelector('.amigo-music-card__play');
|
||
var seekInput = card.querySelector('.amigo-music-card__seek');
|
||
var currentTime = card.querySelector('.amigo-music-card__time--current');
|
||
var durationTime = card.querySelector('.amigo-music-card__time--duration');
|
||
|
||
if (!audioSrc) return;
|
||
|
||
// 不再创建单独的 Audio 对象,使用全局 AudioManager
|
||
// card.__audio = null;
|
||
|
||
// 检查是否应该显示播放状态
|
||
// (由 AudioManager 在 restorePlayingState 中处理)
|
||
|
||
// Play/Pause - 使用 AudioManager
|
||
function togglePlay(e) {
|
||
if (e) e.stopPropagation();
|
||
AudioManager.playMusic(card);
|
||
}
|
||
|
||
if (playBtn) {
|
||
playBtn.addEventListener('click', togglePlay);
|
||
}
|
||
|
||
card.addEventListener('click', togglePlay);
|
||
|
||
// Seek - 使用 AudioManager
|
||
if (seekInput) {
|
||
seekInput.addEventListener('input', function() {
|
||
if (AudioManager.globalAudio && AudioManager.globalAudio.duration) {
|
||
var time = (parseFloat(seekInput.value) / 1000) * AudioManager.globalAudio.duration;
|
||
AudioManager.seekTo(time);
|
||
}
|
||
});
|
||
|
||
// 拖动时暂停更新
|
||
seekInput.addEventListener('mousedown', function() {
|
||
if (AudioManager.globalAudio) {
|
||
AudioManager.globalAudio.__seeking = true;
|
||
}
|
||
});
|
||
|
||
seekInput.addEventListener('mouseup', function() {
|
||
if (AudioManager.globalAudio) {
|
||
AudioManager.globalAudio.__seeking = false;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
function formatTime(seconds) {
|
||
var m = Math.floor(seconds / 60);
|
||
var s = Math.floor(seconds % 60);
|
||
return m + ':' + (s < 10 ? '0' : '') + s;
|
||
}
|
||
|
||
// ============================================================
|
||
// Cleanup functions for PJAX
|
||
// ============================================================
|
||
|
||
function cleanupVoiceBubbles() {
|
||
document.querySelectorAll('.amigo-voice-bubble').forEach(function(bubble) {
|
||
if (bubble.__audio) {
|
||
try {
|
||
bubble.__audio.pause();
|
||
bubble.__audio.src = '';
|
||
bubble.__audio = null;
|
||
} catch (e) {}
|
||
}
|
||
bubble.__voiceBound = false;
|
||
});
|
||
}
|
||
|
||
function cleanupMusicCards() {
|
||
document.querySelectorAll('.amigo-music-card').forEach(function(card) {
|
||
if (card.__audio) {
|
||
try {
|
||
card.__audio.pause();
|
||
card.__audio.src = '';
|
||
card.__audio = null;
|
||
} catch (e) {}
|
||
}
|
||
card.__musicBound = false;
|
||
});
|
||
}
|
||
|
||
function cleanupLivePhotos() {
|
||
document.querySelectorAll('.amigo-livephoto').forEach(function(livePhoto) {
|
||
var video = livePhoto.querySelector('video');
|
||
if (video) {
|
||
try {
|
||
video.pause();
|
||
video.src = '';
|
||
} catch (e) {}
|
||
}
|
||
livePhoto.__liveBound = false;
|
||
});
|
||
}
|
||
|
||
function cleanupAllShortcodes() {
|
||
// 保存当前播放状态到 sessionStorage(音乐不中断)
|
||
AudioManager.saveToSession();
|
||
|
||
// 只清理语音气泡和实况照片(音乐由全局 AudioManager 管理)
|
||
cleanupVoiceBubbles();
|
||
cleanupLivePhotos();
|
||
|
||
// 不再清理音乐卡片,因为音乐由全局 AudioManager 管理
|
||
// cleanupMusicCards(); // 已移除,音乐在 PJAX 切换时继续播放
|
||
}
|
||
|
||
// ============================================================
|
||
// Initialize all shortcodes
|
||
// ============================================================
|
||
|
||
function initAllShortcodes() {
|
||
// 初始化 AudioManager(创建全局音频元素,恢复播放状态)
|
||
AudioManager.init();
|
||
|
||
initLivePhotoShortcodes();
|
||
initVoiceBubbles();
|
||
initMusicCards();
|
||
|
||
// 恢复 PJAX 切换前的播放状态
|
||
setTimeout(function() {
|
||
AudioManager.restorePlayingState();
|
||
}, 100);
|
||
}
|
||
|
||
// DOMContentLoaded
|
||
document.addEventListener('DOMContentLoaded', initAllShortcodes);
|
||
|
||
// Page load
|
||
window.addEventListener('load', initAllShortcodes);
|
||
|
||
// If DOM is already ready (script loaded late), initialize immediately
|
||
if (document.readyState === 'interactive' || document.readyState === 'complete') {
|
||
initAllShortcodes();
|
||
}
|
||
|
||
// PJAX support - cleanup before and re-initialize after page transition
|
||
document.addEventListener('pjax:beforeSend', cleanupAllShortcodes);
|
||
document.addEventListener('pjax:complete', initAllShortcodes);
|
||
document.addEventListener('pjax:success', initAllShortcodes);
|
||
|
||
// Also listen for custom events that some PJAX libraries fire
|
||
document.addEventListener('page:loaded', initAllShortcodes);
|
||
document.addEventListener('page:change', initAllShortcodes);
|
||
document.addEventListener('turbo:load', initAllShortcodes);
|
||
document.addEventListener('turbo:before-render', cleanupAllShortcodes);
|
||
|
||
// History API based navigation
|
||
document.addEventListener('popstate', function() {
|
||
cleanupAllShortcodes();
|
||
setTimeout(initAllShortcodes, 10);
|
||
});
|