153 lines
5.9 KiB
JavaScript
153 lines
5.9 KiB
JavaScript
// OPUS_v932u_UNIVERSAL_X_DOCTRINE v4
|
|
// Add X close button to EVERY fixed/sticky/absolute element
|
|
// Hide on click + persist in localStorage + auto-stack overlaps
|
|
(function(){
|
|
if (window.__opus_x_v4) return;
|
|
window.__opus_x_v4 = true;
|
|
|
|
var NEVER_CLOSE_IDS = ['wevia-chat','weval-chatbot','chat-launcher','nav-main'];
|
|
var NEVER_CLOSE_TAGS = ['NAV','HEADER','HTML','BODY','MAIN','FOOTER'];
|
|
var NEVER_CLOSE_SELECTORS = ['[role="navigation"]','header','footer'];
|
|
|
|
function shouldGetX(el){
|
|
if (!el || !el.tagName) return false;
|
|
if (el.dataset && el.dataset.opusNoX === '1') return false;
|
|
if (NEVER_CLOSE_TAGS.indexOf(el.tagName) >= 0) return false;
|
|
if (el.id && NEVER_CLOSE_IDS.indexOf(el.id) >= 0) return false;
|
|
for (var i=0; i<NEVER_CLOSE_SELECTORS.length; i++){
|
|
try { if (el.matches(NEVER_CLOSE_SELECTORS[i])) return false; } catch(e){}
|
|
}
|
|
var s = getComputedStyle(el);
|
|
if (s.display === 'none' || s.visibility === 'hidden') return false;
|
|
if (parseFloat(s.opacity) < 0.1) return false;
|
|
if (s.position !== 'fixed' && s.position !== 'sticky' && s.position !== 'absolute') return false;
|
|
var r = el.getBoundingClientRect();
|
|
// Only meaningful-size elements
|
|
if (r.width < 80 || r.height < 24) return false;
|
|
// Don't add if parent already has X
|
|
var p = el.parentElement;
|
|
while (p){
|
|
if (p.dataset && p.dataset.opusXAdded === '1') return false;
|
|
p = p.parentElement;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function makeX(el){
|
|
var btn = document.createElement('button');
|
|
btn.className = 'opus-x-btn';
|
|
btn.innerHTML = '\u00d7';
|
|
btn.setAttribute('aria-label','Masquer cet element');
|
|
btn.title = 'Masquer';
|
|
btn.style.cssText = 'position:absolute;top:2px;right:3px;width:22px;height:22px;background:rgba(239,68,68,0.18);color:#ef4444;border:1px solid rgba(239,68,68,0.35);border-radius:50%;font-size:17px;font-weight:700;line-height:1;cursor:pointer;z-index:999999;display:flex;align-items:center;justify-content:center;padding:0;transition:all .15s;backdrop-filter:blur(4px)';
|
|
btn.onmouseover = function(){this.style.background='rgba(239,68,68,0.4)';this.style.color='#fff';this.style.transform='scale(1.1)';};
|
|
btn.onmouseout = function(){this.style.background='rgba(239,68,68,0.18)';this.style.color='#ef4444';this.style.transform='scale(1)';};
|
|
btn.onclick = function(e){
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
el.style.transition = 'opacity .2s, transform .2s';
|
|
el.style.opacity = '0';
|
|
el.style.transform = 'scale(0.95)';
|
|
setTimeout(function(){ el.style.display='none'; }, 220);
|
|
try {
|
|
var k = 'opus-x-hide-' + (el.id || el.className.toString().substring(0,30) || el.tagName);
|
|
localStorage.setItem(k, Date.now());
|
|
} catch(_){}
|
|
};
|
|
return btn;
|
|
}
|
|
|
|
function addXButtons(){
|
|
// ALL elements that might need X
|
|
document.querySelectorAll('div,aside,section,article,a,span').forEach(function(el){
|
|
if (el.dataset.opusXAdded === '1') return;
|
|
if (!shouldGetX(el)) return;
|
|
|
|
// Ensure relative positioning for child absolute
|
|
var s = getComputedStyle(el);
|
|
if (s.position === 'static') el.style.position = 'relative';
|
|
|
|
el.appendChild(makeX(el));
|
|
el.dataset.opusXAdded = '1';
|
|
});
|
|
}
|
|
|
|
function restoreHidden(){
|
|
try {
|
|
document.querySelectorAll('[id]').forEach(function(el){
|
|
var k = 'opus-x-hide-' + el.id;
|
|
var t = localStorage.getItem(k);
|
|
if (t && (Date.now() - parseInt(t)) < 86400000) {
|
|
el.style.display = 'none';
|
|
}
|
|
});
|
|
} catch(_){}
|
|
}
|
|
|
|
function resolveCollisions(){
|
|
// Find all fixed/sticky
|
|
var fixedEls = [];
|
|
document.querySelectorAll('*').forEach(function(el){
|
|
var s = getComputedStyle(el);
|
|
if ((s.position === 'fixed' || s.position === 'sticky') && s.display !== 'none'){
|
|
var r = el.getBoundingClientRect();
|
|
if (r.width > 30 && r.height > 12) fixedEls.push({el:el, r:r, s:s});
|
|
}
|
|
});
|
|
|
|
// Auto-shift small elements that overlap bigger ones
|
|
for (var i=0; i<fixedEls.length; i++){
|
|
for (var j=i+1; j<fixedEls.length; j++){
|
|
var a = fixedEls[i], b = fixedEls[j];
|
|
if (a.el === b.el) continue;
|
|
// skip parent-child
|
|
if (a.el.contains(b.el) || b.el.contains(a.el)) continue;
|
|
|
|
var ox = Math.max(0, Math.min(a.r.right, b.r.right) - Math.max(a.r.left, b.r.left));
|
|
var oy = Math.max(0, Math.min(a.r.bottom, b.r.bottom) - Math.max(a.r.top, b.r.top));
|
|
var oa = ox * oy;
|
|
var min_area = Math.min(a.r.width*a.r.height, b.r.width*b.r.height);
|
|
if (oa > 400 && oa/min_area > 0.25){
|
|
// Smaller one moves
|
|
var smaller = (a.r.width*a.r.height < b.r.width*b.r.height) ? a : b;
|
|
var bigger = (smaller === a) ? b : a;
|
|
// Move smaller out of the way
|
|
// If smaller is at top-right area, push down
|
|
if (smaller.r.top < 100 && smaller.r.right > window.innerWidth - 300){
|
|
smaller.el.style.top = (bigger.r.bottom + 10) + 'px';
|
|
}
|
|
// If right, push further right or down
|
|
else {
|
|
smaller.el.style.right = '12px';
|
|
smaller.el.style.top = (bigger.r.bottom + 10) + 'px';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function run(){
|
|
try { addXButtons(); } catch(e){}
|
|
try { restoreHidden(); } catch(e){}
|
|
try { resolveCollisions(); } catch(e){}
|
|
}
|
|
|
|
if (document.readyState === 'loading'){
|
|
document.addEventListener('DOMContentLoaded', run);
|
|
} else {
|
|
run();
|
|
}
|
|
setTimeout(run, 800);
|
|
setTimeout(run, 2500);
|
|
setTimeout(run, 5000);
|
|
|
|
try {
|
|
var obs = new MutationObserver(function(muts){
|
|
var need = false;
|
|
muts.forEach(function(m){ if (m.addedNodes && m.addedNodes.length) need = true; });
|
|
if (need) setTimeout(run, 100);
|
|
});
|
|
obs.observe(document.body, {childList:true, subtree:true});
|
|
} catch(e){}
|
|
})();
|