- rehydrate.js 新增 boot()/aksrt:ready 事件,支持 boot 后晚注册立即以 init 触发 - aksrt-runtime.js 初始加载改走 AksrtFrontend.boot,tooltip/倒计时注册为组件 - 约 30 个前端脚本(首页、评论、支付、路由、页面叶子、widgets) 从直接 DOMContentLoaded 迁移为 AksrtFrontend.register,并加幂等标记 - section-navs 依赖改为 aksrt-runtime,保证注册时序 - 清理死代码:删除孤儿 loader.js/main.js 及 runtime/page-navs 遗留副本 - loader.js 移除已迁移模块的 tbquire 路径映射,main.js 相应瘦身 - build-public-assets.mjs 增加 runtime/main.min.js 自动压缩步骤 - 新增 4 个契约测试(homepage-dom/rehydrate-runtime/global-dom-init/ public-bundle-parity),3 个既有 harness 适配注册表初始化方式
310 lines
10 KiB
JavaScript
310 lines
10 KiB
JavaScript
/**
|
|
* AKSRT Post Share
|
|
*
|
|
* Handles share dropdowns, social share targets, copy link,
|
|
* and WeChat bridge handoff with QR fallback.
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
(function () {
|
|
'use strict';
|
|
|
|
function getMetaContent(selectors) {
|
|
for (var i = 0; i < selectors.length; i += 1) {
|
|
var node = document.querySelector(selectors[i]);
|
|
var content = node ? node.getAttribute('content') : '';
|
|
if (content) return content;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function getPlatform(node) {
|
|
if (!node) return '';
|
|
return node.getAttribute('data-share-platform') || node.getAttribute('data-platform') || '';
|
|
}
|
|
|
|
function resolvePayload(node) {
|
|
// Unified payload via the shared builder; fall back to local resolution
|
|
// only if the builder script is unavailable.
|
|
if (window.AksrtShareData && typeof window.AksrtShareData.resolve === 'function') {
|
|
return window.AksrtShareData.resolve(node);
|
|
}
|
|
|
|
var dropdown = document.getElementById('share-dropdown');
|
|
var source = node && typeof node.closest === 'function'
|
|
? node.closest('[data-share-url], [data-share-title], [data-share-desc], [data-share-image]')
|
|
: null;
|
|
var context = source || dropdown;
|
|
|
|
function readAttr(name) {
|
|
return (node && node.getAttribute(name)) || (context && context.getAttribute(name)) || '';
|
|
}
|
|
|
|
return {
|
|
title: readAttr('data-share-title') || document.title,
|
|
url: readAttr('data-share-url') || window.location.href,
|
|
desc: readAttr('data-share-desc') || getMetaContent(['meta[property="og:description"]', 'meta[name="description"]']),
|
|
text: readAttr('data-share-text') || '',
|
|
image: readAttr('data-share-image') || getMetaContent(['meta[property="og:image"]']),
|
|
qrcode: readAttr('data-share-qrcode') || ''
|
|
};
|
|
}
|
|
|
|
function openWindow(url) {
|
|
if (!url) return;
|
|
window.open(url, '_blank', 'width=640,height=560,scrollbars=yes,resizable=yes');
|
|
}
|
|
|
|
function buildShareUrl(platform, payload) {
|
|
switch (platform) {
|
|
case 'weibo':
|
|
return 'https://service.weibo.com/share/share.php?url=' + encodeURIComponent(payload.url) + '&title=' + encodeURIComponent(payload.title + (payload.text ? ' ' + payload.text : ''));
|
|
case 'qq':
|
|
return 'https://connect.qq.com/widget/shareqq/index.html?url=' + encodeURIComponent(payload.url) + '&title=' + encodeURIComponent(payload.title) + '&desc=' + encodeURIComponent(payload.desc || payload.text || '');
|
|
case 'twitter':
|
|
return 'https://x.com/intent/tweet?url=' + encodeURIComponent(payload.url) + '&text=' + encodeURIComponent(payload.title);
|
|
case 'telegram':
|
|
return 'https://t.me/share/url?url=' + encodeURIComponent(payload.url) + '&text=' + encodeURIComponent(payload.title);
|
|
case 'linkedin':
|
|
return 'https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(payload.url);
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function fallbackCopy(text) {
|
|
var textarea = document.createElement('textarea');
|
|
textarea.value = text;
|
|
textarea.style.cssText = 'position:fixed;left:-9999px;top:-9999px;opacity:0;';
|
|
textarea.setAttribute('readonly', '');
|
|
document.body.appendChild(textarea);
|
|
try {
|
|
textarea.select();
|
|
textarea.setSelectionRange(0, textarea.value.length);
|
|
if (!document.execCommand('copy')) {
|
|
throw new Error('copy command failed');
|
|
}
|
|
} finally {
|
|
document.body.removeChild(textarea);
|
|
}
|
|
}
|
|
|
|
function copyLink(text) {
|
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
return navigator.clipboard.writeText(text).catch(function () {
|
|
fallbackCopy(text);
|
|
});
|
|
}
|
|
|
|
try {
|
|
fallbackCopy(text);
|
|
return Promise.resolve();
|
|
} catch (error) {
|
|
return Promise.reject(error);
|
|
}
|
|
}
|
|
|
|
function initShareDropdown() {
|
|
var dropdown = document.getElementById('share-dropdown');
|
|
var triggers = Array.prototype.slice.call(document.querySelectorAll('#action-share-trigger, .action-share[data-type="share"], .footer-tabbar .action-share')).filter(function (el, idx, arr) { return arr.indexOf(el) === idx; });
|
|
var trigger = triggers[0] || null;
|
|
if (!trigger || !dropdown || dropdown.dataset.aksrtShareBound === '1') return;
|
|
|
|
dropdown.dataset.aksrtShareBound = '1';
|
|
|
|
var qrBox = document.getElementById('share-qrcode-box');
|
|
var qrImage = document.getElementById('share-qrcode-image');
|
|
|
|
function closeDropdown() {
|
|
dropdown.setAttribute('aria-hidden', 'true');
|
|
triggers.forEach(function (t) { t.setAttribute('aria-expanded', 'false'); });
|
|
document.body.classList.remove('aksrt-share-sheet-open');
|
|
if (window.AksrtShare && typeof window.AksrtShare.hideQrFallback === 'function') {
|
|
window.AksrtShare.hideQrFallback(qrBox);
|
|
} else if (qrBox) {
|
|
qrBox.classList.remove('is-visible');
|
|
qrBox.setAttribute('aria-hidden', 'true');
|
|
}
|
|
}
|
|
|
|
function toggleDropdown() {
|
|
var isOpen = dropdown.getAttribute('aria-hidden') === 'false';
|
|
if (isOpen) {
|
|
closeDropdown();
|
|
return;
|
|
}
|
|
|
|
dropdown.setAttribute('aria-hidden', 'false');
|
|
triggers.forEach(function (t) { t.setAttribute('aria-expanded', 'true'); });
|
|
if (window.matchMedia && window.matchMedia('(max-width: 767px)').matches) {
|
|
document.body.classList.add('aksrt-share-sheet-open');
|
|
}
|
|
}
|
|
|
|
trigger.addEventListener('click', function (event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
toggleDropdown();
|
|
});
|
|
|
|
triggers.slice(1).forEach(function (extraTrigger) {
|
|
extraTrigger.addEventListener('click', function (event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
// Reuse primary trigger click semantics by synthetic toggle
|
|
trigger.click();
|
|
});
|
|
});
|
|
|
|
|
|
document.addEventListener('click', function (event) {
|
|
if (!trigger.contains(event.target) && !dropdown.contains(event.target)) {
|
|
closeDropdown();
|
|
}
|
|
});
|
|
|
|
document.addEventListener('keydown', function (event) {
|
|
if (event.key === 'Escape') {
|
|
closeDropdown();
|
|
}
|
|
});
|
|
|
|
dropdown.addEventListener('click', function (event) {
|
|
var item = event.target.closest('.share-item');
|
|
if (!item) return;
|
|
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
handleShareAction(item, {
|
|
qrBox: qrBox,
|
|
qrImage: qrImage,
|
|
closeDropdown: closeDropdown,
|
|
withinDropdown: true
|
|
});
|
|
});
|
|
}
|
|
|
|
function handleWeixinShare(node, payload, options) {
|
|
options = options || {};
|
|
|
|
var share = window.AksrtShare;
|
|
if (!share || typeof share.openWeixinShare !== 'function') {
|
|
notyf('微信分享模块未加载', 'danger');
|
|
return Promise.resolve({ mode: 'error' });
|
|
}
|
|
|
|
if (
|
|
options.qrBox &&
|
|
options.qrBox.classList.contains('is-visible') &&
|
|
(!share.isWeixinEnv || !share.isWeixinEnv())
|
|
) {
|
|
share.hideQrFallback(options.qrBox);
|
|
return Promise.resolve({ mode: 'closed' });
|
|
}
|
|
|
|
return share.openWeixinShare({
|
|
title: payload.title,
|
|
url: payload.url,
|
|
desc: payload.desc || payload.text || '',
|
|
image: payload.image,
|
|
qrBox: options.qrBox || null,
|
|
qrImage: options.qrImage || null
|
|
}).then(function (result) {
|
|
if (result && result.message) {
|
|
notyf(result.message, result.mode === 'error' ? 'danger' : (result.mode === 'bridge' ? 'success' : 'info'));
|
|
}
|
|
return result;
|
|
});
|
|
}
|
|
|
|
function handleShareAction(node, options) {
|
|
var platform = getPlatform(node);
|
|
if (!platform) return Promise.resolve({ mode: 'noop' });
|
|
|
|
var payload = resolvePayload(node);
|
|
if (!payload.url) return Promise.resolve({ mode: 'noop' });
|
|
|
|
if (platform === 'copy') {
|
|
return copyLink(payload.url).then(function () {
|
|
notyf('链接已复制', 'success');
|
|
if (options && typeof options.closeDropdown === 'function') {
|
|
options.closeDropdown();
|
|
}
|
|
return { mode: 'copy' };
|
|
}).catch(function () {
|
|
notyf('复制失败,请手动复制链接', 'danger');
|
|
return { mode: 'error' };
|
|
});
|
|
}
|
|
|
|
if (platform === 'wechat') {
|
|
return handleWeixinShare(node, payload, options).then(function (result) {
|
|
if (result && result.mode !== 'qr' && result.mode !== 'error' && options && typeof options.closeDropdown === 'function') {
|
|
options.closeDropdown();
|
|
}
|
|
return result;
|
|
});
|
|
}
|
|
|
|
if (platform === 'poster') {
|
|
if (window.AksrtPosterShare && typeof window.AksrtPosterShare.open === 'function') {
|
|
window.AksrtPosterShare.open(payload);
|
|
if (options && typeof options.closeDropdown === 'function') options.closeDropdown();
|
|
return Promise.resolve({ mode: 'poster' });
|
|
}
|
|
notyf('海报模块未加载', 'danger');
|
|
return Promise.resolve({ mode: 'error' });
|
|
}
|
|
|
|
var shareUrl = buildShareUrl(platform, payload);
|
|
if (!shareUrl) {
|
|
return Promise.resolve({ mode: 'noop' });
|
|
}
|
|
|
|
openWindow(shareUrl);
|
|
if (options && typeof options.closeDropdown === 'function') {
|
|
options.closeDropdown();
|
|
}
|
|
|
|
return Promise.resolve({ mode: 'window' });
|
|
}
|
|
|
|
var genericShareBound = false;
|
|
|
|
function bindGenericShareButtons() {
|
|
if (genericShareBound) return;
|
|
genericShareBound = true;
|
|
document.addEventListener('click', function (event) {
|
|
var node = event.target.closest('[data-share-platform]');
|
|
if (!node || node.closest('#share-dropdown')) return;
|
|
|
|
event.preventDefault();
|
|
handleShareAction(node, {});
|
|
});
|
|
|
|
document.addEventListener('click', function (event) {
|
|
var node = event.target.closest('[data-share-copy]');
|
|
if (!node) return;
|
|
|
|
event.preventDefault();
|
|
var url = node.getAttribute('data-share-url') || window.location.href;
|
|
copyLink(url).then(function () {
|
|
notyf('链接已复制', 'success');
|
|
}).catch(function () {
|
|
notyf('复制失败,请手动复制链接', 'danger');
|
|
});
|
|
});
|
|
}
|
|
|
|
function init() {
|
|
initShareDropdown();
|
|
bindGenericShareButtons();
|
|
}
|
|
|
|
window.AksrtFrontend.register('aksrt-post-share', function () {
|
|
init();
|
|
});
|
|
})();
|