2023-08-26 17:32:58 +02:00
|
|
|
export { registerReactivity } from './signals-common.js';
|
|
|
|
|
2023-09-09 21:15:43 +02:00
|
|
|
export function dispatchEvent(element, name, ...d){
|
|
|
|
const event= d.length ? new CustomEvent(name, { detail: d[0] }) : new Event(name);
|
|
|
|
return element.dispatchEvent(event);
|
|
|
|
}
|
2023-08-25 12:04:15 +02:00
|
|
|
export function on(event, listener, options){
|
2023-09-09 21:15:43 +02:00
|
|
|
return function registerElement(element){
|
2023-09-05 09:25:47 +02:00
|
|
|
element.addEventListener(event, listener, options);
|
|
|
|
return element;
|
|
|
|
};
|
2023-08-23 15:37:32 +02:00
|
|
|
}
|
2023-08-25 12:04:58 +02:00
|
|
|
|
2023-09-08 15:54:47 +02:00
|
|
|
const c_ch_o= connectionsChangesObserverConstructor();
|
2023-10-13 16:37:04 +02:00
|
|
|
const els_attribute_store= new WeakSet();
|
2023-09-09 21:15:43 +02:00
|
|
|
import { onAbort } from './helpers.js';
|
|
|
|
//TODO: cleanUp when event before abort?
|
2023-09-05 09:25:47 +02:00
|
|
|
on.connected= function(listener, options){
|
2023-10-13 16:37:04 +02:00
|
|
|
const name= "connected";
|
2023-10-09 13:49:38 +02:00
|
|
|
if(typeof options !== "object")
|
|
|
|
options= {};
|
|
|
|
options.once= true;
|
2023-08-25 12:04:58 +02:00
|
|
|
return function registerElement(element){
|
2023-10-13 16:37:04 +02:00
|
|
|
const event= "dde:"+name;
|
|
|
|
element.addEventListener(event, listener, options);
|
|
|
|
if(typeof element[name+"Callback"] === "function") return element;
|
2023-10-09 13:49:38 +02:00
|
|
|
if(element.isConnected){
|
2023-10-13 16:37:04 +02:00
|
|
|
element.dispatchEvent(new Event(event));
|
2023-09-11 18:31:23 +02:00
|
|
|
return element;
|
|
|
|
}
|
2023-10-09 13:49:38 +02:00
|
|
|
|
|
|
|
const c= onAbort(options.signal, ()=> c_ch_o.offConnected(element, listener));
|
|
|
|
if(c) c_ch_o.onConnected(element, listener);
|
2023-09-09 21:15:43 +02:00
|
|
|
return element;
|
2023-08-25 12:04:58 +02:00
|
|
|
};
|
|
|
|
};
|
2023-09-05 09:25:47 +02:00
|
|
|
on.disconnected= function(listener, options){
|
2023-10-13 16:37:04 +02:00
|
|
|
const name= "disconnected";
|
2023-10-09 13:49:38 +02:00
|
|
|
if(typeof options !== "object")
|
|
|
|
options= {};
|
|
|
|
options.once= true;
|
2023-08-25 12:04:58 +02:00
|
|
|
return function registerElement(element){
|
2023-10-13 16:37:04 +02:00
|
|
|
const event= "dde:"+name;
|
|
|
|
element.addEventListener(event, listener, options);
|
|
|
|
if(typeof element[name+"Callback"] === "function") return element;
|
2023-10-09 13:49:38 +02:00
|
|
|
|
|
|
|
const c= onAbort(options.signal, ()=> c_ch_o.offDisconnected(element, listener));
|
2023-09-09 21:15:43 +02:00
|
|
|
if(c) c_ch_o.onDisconnected(element, listener);
|
|
|
|
return element;
|
2023-08-25 12:04:58 +02:00
|
|
|
};
|
|
|
|
};
|
2023-10-13 16:37:04 +02:00
|
|
|
on.attributeChanged= function(listener, options){
|
|
|
|
const name= "attributeChanged";
|
|
|
|
if(typeof options !== "object")
|
|
|
|
options= {};
|
|
|
|
return function registerElement(element){
|
|
|
|
const event= "dde:"+name;
|
|
|
|
element.addEventListener(event, listener, options);
|
|
|
|
if(typeof element[name+"Callback"] === "function") return element;
|
|
|
|
if(els_attribute_store.has(element)) return element;
|
|
|
|
|
|
|
|
const observer= new MutationObserver(function(mutations){
|
|
|
|
for(const { attributeName, target } of mutations)
|
|
|
|
target.dispatchEvent(
|
|
|
|
new CustomEvent(event, { detail: [ attributeName, target.getAttribute(attributeName) ] }));
|
|
|
|
});
|
|
|
|
const c= onAbort(options.signal, ()=> observer.disconnect());
|
|
|
|
if(c) observer.observe(element, { attributes: true });
|
|
|
|
return element;
|
|
|
|
};
|
|
|
|
};
|
2023-08-25 12:04:58 +02:00
|
|
|
|
|
|
|
function connectionsChangesObserverConstructor(){
|
|
|
|
const store= new Map();
|
|
|
|
let is_observing= false;
|
|
|
|
const observer= new MutationObserver(function(mutations){
|
|
|
|
for(const mutation of mutations){
|
|
|
|
if(mutation.type!=="childList") continue;
|
2023-08-26 17:47:46 +02:00
|
|
|
if(observerAdded(mutation.addedNodes, true)){
|
2023-08-25 12:04:58 +02:00
|
|
|
stop();
|
|
|
|
continue;
|
|
|
|
}
|
2023-08-26 17:47:46 +02:00
|
|
|
if(observerRemoved(mutation.removedNodes, true))
|
2023-08-25 12:04:58 +02:00
|
|
|
stop();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return {
|
|
|
|
onConnected(element, listener){
|
|
|
|
start();
|
|
|
|
const listeners= getElementStore(element);
|
2023-10-09 13:49:38 +02:00
|
|
|
if(listeners.connected.has(listener)) return;
|
|
|
|
listeners.connected.add(listener);
|
|
|
|
listeners.length_c+= 1;
|
2023-08-25 12:04:58 +02:00
|
|
|
},
|
|
|
|
offConnected(element, listener){
|
|
|
|
if(!store.has(element)) return;
|
|
|
|
const ls= store.get(element);
|
2023-10-09 13:49:38 +02:00
|
|
|
if(!ls.connected.has(listener)) return;
|
|
|
|
ls.connected.delete(listener);
|
|
|
|
ls.length_c-= 1;
|
2023-08-25 12:04:58 +02:00
|
|
|
cleanWhenOff(element, ls);
|
|
|
|
},
|
|
|
|
onDisconnected(element, listener){
|
|
|
|
start();
|
|
|
|
const listeners= getElementStore(element);
|
2023-10-09 13:49:38 +02:00
|
|
|
if(listeners.disconnected.has(listener)) return;
|
|
|
|
listeners.disconnected.add(listener);
|
|
|
|
listeners.length_d+= 1;
|
2023-08-25 12:04:58 +02:00
|
|
|
},
|
|
|
|
offDisconnected(element, listener){
|
|
|
|
if(!store.has(element)) return;
|
|
|
|
const ls= store.get(element);
|
2023-10-09 13:49:38 +02:00
|
|
|
if(!ls.disconnected.has(listener)) return;
|
|
|
|
ls.disconnected.delete(listener);
|
|
|
|
ls.length_d-= 1;
|
2023-08-25 12:04:58 +02:00
|
|
|
cleanWhenOff(element, ls);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
function cleanWhenOff(element, ls){
|
2023-10-09 13:49:38 +02:00
|
|
|
if(ls.length_c || ls.length_d)
|
2023-08-25 12:04:58 +02:00
|
|
|
return;
|
|
|
|
store.delete(element);
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
function getElementStore(element){
|
|
|
|
if(store.has(element)) return store.get(element);
|
2023-10-09 13:49:38 +02:00
|
|
|
const out= {
|
|
|
|
connected: new WeakSet(),
|
|
|
|
length_c: 0,
|
|
|
|
disconnected: new WeakSet(),
|
|
|
|
length_d: 0
|
|
|
|
};
|
2023-08-25 12:04:58 +02:00
|
|
|
store.set(element, out);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
function start(){
|
|
|
|
if(is_observing) return;
|
|
|
|
is_observing= true;
|
|
|
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
|
|
}
|
|
|
|
function stop(){
|
|
|
|
if(!is_observing || store.size) return;
|
|
|
|
is_observing= false;
|
|
|
|
observer.disconnect();
|
|
|
|
}
|
|
|
|
//TODO remount support?
|
|
|
|
function requestIdle(){ return new Promise(function(resolve){
|
|
|
|
(requestIdleCallback || requestAnimationFrame)(resolve);
|
|
|
|
}); }
|
|
|
|
async function collectChildren(element){
|
|
|
|
if(store.size > 30)//TODO limit?
|
|
|
|
await requestIdle();
|
|
|
|
const out= [];
|
|
|
|
if(!(element instanceof Node)) return out;
|
|
|
|
for(const el of store.keys()){
|
|
|
|
if(el===element || !(el instanceof Node)) continue;
|
|
|
|
if(element.contains(el))
|
|
|
|
out.push(el);
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
2023-08-26 17:47:46 +02:00
|
|
|
function observerAdded(addedNodes, is_root){
|
2023-10-09 13:49:38 +02:00
|
|
|
let out= false;
|
2023-08-25 12:04:58 +02:00
|
|
|
for(const element of addedNodes){
|
2023-08-26 17:47:46 +02:00
|
|
|
if(is_root) collectChildren(element).then(observerAdded);
|
2023-09-09 21:15:43 +02:00
|
|
|
if(!store.has(element)) continue;
|
2023-08-25 12:04:58 +02:00
|
|
|
|
|
|
|
const ls= store.get(element);
|
2023-10-09 13:49:38 +02:00
|
|
|
if(!ls.length_c) continue;
|
|
|
|
|
|
|
|
element.dispatchEvent(new Event("dde:connected"));
|
|
|
|
ls.connected= new WeakSet();
|
|
|
|
ls.length_c= 0;
|
|
|
|
if(!ls.length_d) store.delete(element);
|
|
|
|
out= true;
|
2023-08-25 12:04:58 +02:00
|
|
|
}
|
2023-10-09 13:49:38 +02:00
|
|
|
return out;
|
2023-08-25 12:04:58 +02:00
|
|
|
}
|
2023-08-26 17:47:46 +02:00
|
|
|
function observerRemoved(removedNodes, is_root){
|
2023-10-09 13:49:38 +02:00
|
|
|
let out= false;
|
2023-08-25 12:04:58 +02:00
|
|
|
for(const element of removedNodes){
|
2023-08-26 17:47:46 +02:00
|
|
|
if(is_root) collectChildren(element).then(observerRemoved);
|
2023-09-09 21:15:43 +02:00
|
|
|
if(!store.has(element)) continue;
|
2023-08-25 12:04:58 +02:00
|
|
|
|
|
|
|
const ls= store.get(element);
|
2023-10-09 13:49:38 +02:00
|
|
|
if(!ls.length_d) continue;
|
|
|
|
|
|
|
|
element.dispatchEvent(new Event("dde:disconnected"));
|
2023-08-25 12:04:58 +02:00
|
|
|
|
|
|
|
store.delete(element);
|
2023-10-09 13:49:38 +02:00
|
|
|
out= true;
|
2023-08-25 12:04:58 +02:00
|
|
|
}
|
2023-10-09 13:49:38 +02:00
|
|
|
return out;
|
2023-08-25 12:04:58 +02:00
|
|
|
}
|
|
|
|
}
|