// ==UserScript==
// @name IframeManager
// @version 2026.02.15.01
// @description 修复预览bug
// @author You
// @include *
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_getValue
// @grant GM_download
// @grant GM_xmlhttpRequest
// @require https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.5.1.min.js
// ==/UserScript==
class IframeManager{
box = new IframeManagerBox();
private = {
promise : Promise.resolve(),
}
constructor(origin){
if(!origin) throw("Must set origin");
this.box.AddBox();
this.Post = new IframePoster(origin);
}
async Open(url) {
let $iframe;
this.private.count++;
this.private.promise = this.private.promise.then(async () => {
$iframe = await this.box.AddIframe(url);
this.OnOpened($iframe[0]);
$iframe.on('load', () => this.OnLoaded($iframe[0]))
});
await this.private.promise;
}
Close(url){
this.private.count--;
this.box.RemoveIframe(url);
}
OnOpened(iframe){}
OnLoaded(iframe){}
Post = null;
}
class IframeManagerBox{
static addEvented = false;
funul = null;
_count = 0;
_Counter(v){
this._count+=v;
$('.iframe-manager .swith').css('--count',`"${this._count}"`);
}
AddBox(){
const box = `
`
$('body').append(box);
this._Event()
}
_Event(){
const _this = this;
if(IframeManagerBox.addEvented) return;
IframeManagerBox.addEvented = true;
this.funul = new FunUL($('.iframe-manager .item:first')[0],"{{url}}");
$('.iframe-manager')
.on('click','.item .close',function(event){
const url = $(event.target).data('url');
if(url) _this.RemoveIframe(url);
})
.on('click','.item .retry',function(event){
const iframe = $(event.target).nextAll("iframe");
if(iframe.length==1) iframe[0].src = iframe[0].src;
})
$('.iframe-manager .swith').click(function(){
$('.iframe-manager').toggleClass('show');
})
$('.iframe-manager .swith').on('mouseenter',()=>{
if(!$('.iframe-manager').hasClass('show')) $('.iframe-manager').toggleClass('show');
})
$('.iframe-manager .item').hide();
}
async AddIframe(url){
this._Counter(1);
return this.funul.Add(url).then($item=>{
$item.find('button').data('url',url);
return $item.find('iframe').show();
});
}
RemoveIframe(url){
this._Counter(-1);
this.funul.Remove(url);
}
}
class FunUL{
items = new DictionaryList();
/**
* @example new FunUL($('.iframe-manager .item:first')[0],"{{url}}");
*/
constructor(templateDom,itemKey){
this.template = templateDom.outerHTML;
this.container = $(templateDom).parent();
this.itemKey = itemKey;
}
async Add(key,values = []){console.log("template",this.template);console.log("itemkey",this.itemKey)
let dom = this.template;
dom = dom.replace(this.itemKey,key);
values.forEach(info=>{
const k = info.key instanceof RegExp && info.key.global
? info.key
: new RegExp(info.key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
dom = dom.replace(k, info.value);
});console.log("key",key);console.log("dom",dom);
dom = $(dom);
this.container.append(dom);
await this.items.Push(key,dom);
return dom;
}
async Remove(key){
this.items.Get(key).forEach($item=>$item.remove());
await this.items.Del(key);
}
}
class DictionaryList {
_promise = Promise.resolve();
_value = {};
async Push(key, value) {
this._promise = this._promise.then(() => {
if (!this._value[key]) this._value[key] = [value];
else this._value[key].push(value);
});
await this._promise;
}
async Del(key) {
this._promise = this._promise.then(() => {
delete this._value[key]; // ✅ 彻底删除
});
await this._promise;
}
Get(key){
console.log(this._value);
console.log(key,this._value[key]);
return this._value[key];
}
}
class IframePoster{
static listenerAdded = false;
origin = "*";
constructor(origin,CanIPost) {
this.origin = origin;
if(IframePoster.listenerAdded) return;
IframePoster.listenerAdded = true;
window.addEventListener('message', (e) => {
if(CanIPost && !CanIPost(e.origin)) return;
if (e.data.type === 'ParentPost') this.onFromParent(e.data.data);
else if (e.data.type === 'iframeReady') this.onFromIframe(e.data.data);
});
}
ToIframe(iframe,data){
iframe.contentWindow.postMessage(
{ type: 'ParentPost', data: data},
iframe.src
);
}
ToParent(data){
parent.postMessage(
{ type: 'iframeReady', data: data },
this.origin
);
}
onFromIframe(data){}
onFromParent(data){}
}
/**
* @description 用户只需要Open创建并重写async IframeAction(url){}就行
*/
class IframeManagerExtend1 extends IframeManager{
constructor(origin){
super(origin);
this._IframePageLinstener();
this._ParentPageLinstener();
}
OnLoaded(iframe){
$(iframe).data('url',iframe.src);
this.Post.ToIframe(iframe,{autoAction:true,url:iframe.src});
}
_IframePageLinstener(){
this.Post.onFromParent = async data =>{
if(!data.autoAction) return;
await this.IframeAction(data.url);
this.OnIframeActionDone(data.url);
}
}
_ParentPageLinstener(){
this.Post.onFromIframe = data =>{
if(!data.ActionDone) return;
this.Close(data.ActionDone);
}
}
async IframeAction(url){
throw new Error('IframeAction must be override');
}
OnIframeActionDone(url){
this.Post.ToParent({ActionDone:url})
}
}
window.IframeManager = IframeManager;
window.FunUL = FunUL;
window.DictionaryList = DictionaryList;
window.IframePoster = IframePoster;
window.IframeManagerExtend1 = IframeManagerExtend1;