e2ad3bf62b
- 新增 public share widget 与 workspace relative path 解析 - 增强 publications/chat-plaza 发布链路与缩略图 demo - 补充 schema、cover 检查脚本与回归测试 Co-authored-by: Cursor <cursoragent@cursor.com>
209 lines
7.4 KiB
JavaScript
209 lines
7.4 KiB
JavaScript
/**
|
|
* Plaza iframe embed helpers — full-page iframe height sync for publication pages.
|
|
*/
|
|
|
|
export const PLAZA_EMBED_QUERY = 'embed=plaza';
|
|
|
|
export function isPlazaEmbedRequest(query = {}) {
|
|
return String(query?.embed ?? '').toLowerCase() === 'plaza';
|
|
}
|
|
|
|
export function publishedPageCspForEmbed(isFullHtml) {
|
|
if (isFullHtml) {
|
|
return "default-src 'none'; style-src 'unsafe-inline' https:; img-src 'self' data: https: http:; font-src https: data:; base-uri 'none'; form-action 'self'; frame-ancestors *; script-src 'unsafe-inline'";
|
|
}
|
|
return "default-src 'none'; style-src 'unsafe-inline'; img-src data:; font-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors *; script-src 'unsafe-inline'";
|
|
}
|
|
|
|
export function allowPlazaEmbedFrame(res) {
|
|
res.removeHeader?.('X-Frame-Options');
|
|
}
|
|
|
|
const EMBED_BOOTSTRAP = `<script id="plaza-embed-bootstrap">(function(){
|
|
if(window.parent===window)return;
|
|
var d=document;
|
|
function isDecorative(el){
|
|
if(!el||el.nodeType!==1)return true;
|
|
var cls=String(el.className||'');
|
|
if(/\b(bg-particles|glow-orb|particle|backdrop|ambient|bg-glow)\b/i.test(cls))return true;
|
|
var st=window.getComputedStyle(el);
|
|
if(st.position==='fixed')return true;
|
|
if(st.position==='absolute'&&(Number(st.zIndex||0)<=0||st.pointerEvents==='none'))return true;
|
|
return false;
|
|
}
|
|
function blocks(){
|
|
var out=[],hero=d.querySelector('.hero');
|
|
if(hero)out.push(hero);
|
|
d.querySelectorAll('.section,.quote-section,.container,main,article').forEach(function(el){out.push(el);});
|
|
var foot=d.querySelector('.footer');
|
|
if(foot)out.push(foot);
|
|
return out;
|
|
}
|
|
function fullHeight(){
|
|
var body=d.body,root=d.documentElement;
|
|
var measured=0;
|
|
var scrollTop=window.pageYOffset||(root?root.scrollTop:0)||(body?body.scrollTop:0)||0;
|
|
function bottomOf(el){
|
|
if(!el)return 0;
|
|
var rect=el.getBoundingClientRect();
|
|
return rect.bottom+scrollTop;
|
|
}
|
|
var els=blocks();
|
|
for(var i=0;i<els.length;i++){
|
|
if(!els[i]||isDecorative(els[i]))continue;
|
|
measured=Math.max(measured,bottomOf(els[i]),els[i].offsetTop+els[i].offsetHeight);
|
|
}
|
|
if(body){
|
|
for(var j=0;j<body.children.length;j++){
|
|
var child=body.children[j];
|
|
if(isDecorative(child))continue;
|
|
measured=Math.max(measured,bottomOf(child),child.offsetTop+child.offsetHeight);
|
|
}
|
|
}
|
|
if(body&&body.lastElementChild&&!isDecorative(body.lastElementChild)){
|
|
var last=body.lastElementChild;
|
|
measured=Math.max(measured,bottomOf(last),last.offsetTop+last.offsetHeight);
|
|
}
|
|
return Math.max(Math.ceil(measured),420);
|
|
}
|
|
function restoreBlock(block){
|
|
if(!block)return;
|
|
block.querySelectorAll('[data-plaza-defer-bg]').forEach(function(el){
|
|
el.style.backgroundImage=el.dataset.plazaDeferBg;
|
|
delete el.dataset.plazaDeferBg;
|
|
});
|
|
block.querySelectorAll('[data-plaza-defer-hero-bg]').forEach(function(el){
|
|
var bg=el.dataset.plazaDeferHeroBg;
|
|
if(!bg)return;
|
|
if(bg.indexOf('url(')>=0)el.style.backgroundImage=bg;
|
|
else el.style.background=bg;
|
|
delete el.dataset.plazaDeferHeroBg;
|
|
});
|
|
}
|
|
function reportHeight(){
|
|
var b=blocks();
|
|
var h=fullHeight();
|
|
window.name='plaza-height:'+h;
|
|
var payload={
|
|
type:'plaza:embed-section',
|
|
count:b.length,
|
|
height:h,
|
|
total:b.length
|
|
};
|
|
window.parent.postMessage(JSON.stringify(payload),'*');
|
|
window.parent.postMessage({
|
|
type:'plaza:embed-section',
|
|
count:payload.count,
|
|
height:h,
|
|
total:payload.total
|
|
},'*');
|
|
}
|
|
function showAll(){
|
|
var b=blocks();
|
|
if(!b.length&&d.body)b=[d.body];
|
|
for(var i=0;i<b.length;i++){
|
|
b[i].style.display='';
|
|
b[i].style.visibility='';
|
|
restoreBlock(b[i]);
|
|
}
|
|
reportHeight();
|
|
}
|
|
var style=d.createElement('style');
|
|
style.textContent='html,body{margin:0;overflow:hidden!important}';
|
|
d.head.appendChild(style);
|
|
function scheduleReport(){
|
|
showAll();
|
|
requestAnimationFrame(function(){
|
|
requestAnimationFrame(function(){
|
|
reportHeight();
|
|
});
|
|
});
|
|
}
|
|
if(d.readyState==='loading'){
|
|
d.addEventListener('DOMContentLoaded',scheduleReport);
|
|
}else{
|
|
scheduleReport();
|
|
}
|
|
window.addEventListener('load',function(){scheduleReport();});
|
|
window.parent&&window.parent.postMessage({
|
|
type:'plaza:embed-ready',
|
|
count:1,
|
|
ready:true,
|
|
height:fullHeight(),
|
|
total:1
|
|
},'*');
|
|
window.addEventListener('message',function(event){
|
|
var data=event&&event.data;
|
|
if(data==='plaza:embed-measure') {
|
|
scheduleReport();
|
|
return;
|
|
}
|
|
if(data&&typeof data==='object'&&data.type==='plaza:embed-measure')scheduleReport();
|
|
});
|
|
if(typeof ResizeObserver!=='undefined'){
|
|
var ro=new ResizeObserver(function(){reportHeight();});
|
|
function observeBlocks(){
|
|
blocks().forEach(function(el){if(el)ro.observe(el);});
|
|
}
|
|
observeBlocks();
|
|
if(typeof MutationObserver!=='undefined'){
|
|
new MutationObserver(function(){
|
|
observeBlocks();
|
|
scheduleReport();
|
|
}).observe(d.documentElement,{childList:true,subtree:true,attributes:true});
|
|
}
|
|
} else if(typeof MutationObserver!=='undefined'){
|
|
new MutationObserver(function(){scheduleReport();}).observe(d.documentElement,{childList:true,subtree:true,attributes:true});
|
|
}
|
|
d.querySelectorAll('img,video,iframe').forEach(function(el){
|
|
el.addEventListener('load',scheduleReport);
|
|
el.addEventListener('error',scheduleReport);
|
|
});
|
|
[100,350,700,1400,2600,5200,9200,15000].forEach(function(delay){setTimeout(scheduleReport,delay);});
|
|
[120,400,1000,2000,4000,8000,12000].forEach(function(delay){setTimeout(scheduleReport,delay);});
|
|
var keepalive=0;
|
|
var keepaliveTimer=setInterval(function(){
|
|
keepalive+=1;
|
|
scheduleReport();
|
|
if(keepalive>=30)clearInterval(keepaliveTimer);
|
|
},1000);
|
|
window.addEventListener('pageshow',scheduleReport);
|
|
window.addEventListener('orientationchange',function(){setTimeout(scheduleReport,300);});
|
|
})();</script>`;
|
|
|
|
const EMBED_LAYOUT_OVERRIDES =
|
|
'<style id="plaza-embed-overrides">.section,.quote-section,.footer{content-visibility:visible!important;contain-intrinsic-size:auto!important}.hero{height:auto!important;min-height:0!important;max-height:none!important;overflow:visible!important}</style>';
|
|
|
|
/** Remove inline CSP meta tags that block plaza-embed-bootstrap (HTTP CSP header applies instead). */
|
|
export function stripPublicationHtmlCspMeta(html) {
|
|
return String(html ?? '').replace(
|
|
/<meta\s+http-equiv\s*=\s*["']Content-Security-Policy["'][^>]*>/gi,
|
|
'',
|
|
);
|
|
}
|
|
|
|
/** Prepare stored publication HTML for Plaza iframe embed. */
|
|
export function preparePublicationHtmlForEmbed(html) {
|
|
let source = stripPublicationHtmlCspMeta(html);
|
|
if (!source.includes('id="plaza-embed-overrides"') && /<head[^>]*>/i.test(source)) {
|
|
source = source.replace(/<head([^>]*)>/i, `<head$1>${EMBED_LAYOUT_OVERRIDES}`);
|
|
}
|
|
return injectPlazaEmbedBootstrap(source);
|
|
}
|
|
|
|
export function injectPlazaEmbedBootstrap(html) {
|
|
const source = String(html ?? '');
|
|
if (source.includes('id="plaza-embed-bootstrap"')) return source;
|
|
if (/<\/body>/i.test(source)) {
|
|
return source.replace(/<\/body>/i, `${EMBED_BOOTSTRAP}</body>`);
|
|
}
|
|
return `${source}${EMBED_BOOTSTRAP}`;
|
|
}
|
|
|
|
export function appendPlazaEmbedQuery(url) {
|
|
const value = String(url ?? '').trim();
|
|
if (!value) return value;
|
|
if (value.includes(PLAZA_EMBED_QUERY)) return value;
|
|
return value.includes('?') ? `${value}&${PLAZA_EMBED_QUERY}` : `${value}?${PLAZA_EMBED_QUERY}`;
|
|
}
|