mirror of
https://git.boykissers.com/pawkey/pawkey-sk.git
synced 2025-12-20 04:04:16 +00:00
318 lines
9.5 KiB
Vue
318 lines
9.5 KiB
Vue
<script setup lang="ts">
|
|
import { $i } from "@/i.js";
|
|
import { computed, onMounted, onUnmounted, ref } from "vue";
|
|
|
|
const generateRandomClass = () => {
|
|
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
let result = "";
|
|
for (let i = 0; i < 8; i++) {
|
|
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
}
|
|
return result;
|
|
};
|
|
|
|
const containerClass = ref(generateRandomClass());
|
|
const recreationKey = ref(0);
|
|
let regenerationInterval: ReturnType<typeof setInterval> | null = null;
|
|
let checkInterval: ReturnType<typeof setInterval> | null = null;
|
|
let styleElement: HTMLStyleElement | null = null;
|
|
let watermarkElement: HTMLDivElement | null = null;
|
|
let mutationObserver: MutationObserver | null = null;
|
|
let headObserver: MutationObserver | null = null;
|
|
let attributeObserver: MutationObserver | null = null;
|
|
let allCreatedClasses: string[] = [];
|
|
|
|
const encodeTextToPath = (text: string) => {
|
|
const charPaths: { [key: string]: string } = {
|
|
a: "M2,8 L4,2 L6,2 L8,8 M3,6 L7,6",
|
|
b: "M2,2 L2,8 L6,8 C7,8 8,7 8,6 C8,5 7,4 6,4 L6,4 C7,4 8,3 8,2 C8,1 7,2 6,2 Z",
|
|
c: "M8,3 C7,2 6,2 5,2 C3,2 2,3 2,5 C2,7 3,8 5,8 C6,8 7,8 8,7",
|
|
d: "M2,2 L2,8 L5,8 C7,8 8,7 8,5 C8,3 7,2 5,2 Z",
|
|
e: "M2,2 L2,8 L8,8 M2,2 L8,2 M2,5 L6,5",
|
|
f: "M2,2 L2,8 M2,2 L8,2 M2,5 L6,5",
|
|
g: "M8,3 C7,2 6,2 5,2 C3,2 2,3 2,5 C2,7 3,8 5,8 L8,8 L8,5 L5,5",
|
|
h: "M2,2 L2,8 M8,2 L8,8 M2,5 L8,5",
|
|
i: "M3,2 L7,2 M5,2 L5,8 M3,8 L7,8",
|
|
j: "M8,2 L8,7 C8,8 7,8 6,8 C5,8 4,8 4,7",
|
|
k: "M2,2 L2,8 M8,2 L2,5 M4,5 L8,8",
|
|
l: "M2,2 L2,8 L8,8",
|
|
m: "M2,8 L2,2 L5,5 L8,2 L8,8",
|
|
n: "M2,8 L2,2 L8,8 L8,2",
|
|
o: "M2,3 C2,2 3,2 5,2 C7,2 8,2 8,3 L8,7 C8,8 7,8 5,8 C3,8 2,8 2,7 Z",
|
|
p: "M2,2 L2,8 M2,2 L6,2 C7,2 8,3 8,4 C8,5 7,6 6,6 L2,6",
|
|
q: "M2,3 C2,2 3,2 5,2 C7,2 8,2 8,3 L8,7 C8,8 7,8 5,8 C3,8 2,8 2,7 Z M6,6 L8,8",
|
|
r: "M2,2 L2,8 M2,2 L6,2 C7,2 8,3 8,4 C8,5 7,6 6,6 L2,6 M6,6 L8,8",
|
|
s: "M8,3 C7,2 5,2 5,2 C3,2 2,3 2,4 C2,5 3,5 5,5 C7,5 8,6 8,7 C8,8 7,8 5,8 C3,8 2,7 2,7",
|
|
t: "M2,2 L8,2 M5,2 L5,8",
|
|
u: "M2,2 L2,7 C2,8 3,8 5,8 C7,8 8,8 8,7 L8,2",
|
|
v: "M2,2 L5,8 L8,2",
|
|
w: "M2,2 L3,8 L5,5 L7,8 L8,2",
|
|
x: "M2,2 L8,8 M8,2 L2,8",
|
|
y: "M2,2 L5,5 L8,2 M5,5 L5,8",
|
|
z: "M2,2 L8,2 L2,8 L8,8",
|
|
" ": "",
|
|
"0": "M2,3 C2,2 3,2 5,2 C7,2 8,2 8,3 L8,7 C8,8 7,8 5,8 C3,8 2,8 2,7 Z M2,2 L8,8",
|
|
"1": "M4,2 L5,2 L5,8 M3,8 L7,8",
|
|
"2": "M2,3 C2,2 3,2 5,2 C7,2 8,2 8,3 C8,4 7,5 5,5 L2,8 L8,8",
|
|
"3": "M2,2 L8,2 C8,2 8,3 8,4 C8,5 7,5 5,5 C7,5 8,5 8,6 C8,7 8,8 8,8 L2,8",
|
|
"4": "M2,2 L2,5 L8,5 M6,2 L6,8",
|
|
"5": "M8,2 L2,2 L2,5 L6,5 C7,5 8,6 8,7 C8,8 7,8 5,8 C3,8 2,7 2,7",
|
|
"6": "M8,2 C7,2 5,2 5,2 C3,2 2,3 2,5 L2,7 C2,8 3,8 5,8 C7,8 8,7 8,6 C8,5 7,5 5,5 L2,5",
|
|
"7": "M2,2 L8,2 L5,8",
|
|
"8": "M5,2 C3,2 2,3 2,4 C2,5 3,5 5,5 C7,5 8,5 8,4 C8,3 7,2 5,2 Z M5,5 C3,5 2,6 2,7 C2,8 3,8 5,8 C7,8 8,7 8,6 C8,5 7,5 5,5 Z",
|
|
"9": "M8,5 C8,3 7,2 5,2 C3,2 2,3 2,4 C2,5 3,5 5,5 L8,5 L8,7 C8,8 7,8 5,8 C3,8 2,7 2,7",
|
|
};
|
|
|
|
let path = "";
|
|
const chars = text.toLowerCase().split("");
|
|
let xOffset = 0;
|
|
|
|
chars.forEach((char, index) => {
|
|
if (charPaths[char]) {
|
|
const translatedPath = charPaths[char]
|
|
.replace(/([ML])\s*(\d+),(\d+)/g, (match, command, x, y) => {
|
|
return `${command}${parseInt(x) + xOffset},${y}`;
|
|
})
|
|
.replace(
|
|
/([C])\s*(\d+),(\d+)\s+(\d+),(\d+)\s+(\d+),(\d+)/g,
|
|
(match, command, x1, y1, x2, y2, x3, y3) => {
|
|
return `${command}${parseInt(x1) + xOffset},${y1} ${parseInt(x2) + xOffset},${y2} ${parseInt(x3) + xOffset},${y3}`;
|
|
},
|
|
);
|
|
path += translatedPath + " ";
|
|
}
|
|
xOffset += 7;
|
|
});
|
|
|
|
return path.trim();
|
|
};
|
|
|
|
const regenerateWatermark = () => {
|
|
cleanupOldElements();
|
|
containerClass.value = generateRandomClass();
|
|
recreationKey.value++;
|
|
allCreatedClasses.push(containerClass.value);
|
|
createStyleElement();
|
|
};
|
|
|
|
const cleanupOldElements = () => {
|
|
const allStyles = document.head.querySelectorAll("style");
|
|
allStyles.forEach((style) => {
|
|
const content = style.textContent || "";
|
|
const hasOldClasses = allCreatedClasses.some(
|
|
(className) =>
|
|
content.includes(className) && className !== containerClass.value,
|
|
);
|
|
if (hasOldClasses) {
|
|
style.remove();
|
|
}
|
|
});
|
|
|
|
allCreatedClasses = [containerClass.value];
|
|
};
|
|
|
|
const createStyleElement = () => {
|
|
if (styleElement && styleElement.parentNode) {
|
|
styleElement.parentNode.removeChild(styleElement);
|
|
}
|
|
|
|
styleElement = document.createElement("style");
|
|
|
|
const fgColor = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-fg').trim() || '#000000';
|
|
|
|
const svgContent = `<svg viewBox="0 0 ${textWidth.value} 10" xmlns="http://www.w3.org/2000/svg"><path d="${pathData.value}" stroke="${fgColor}" fill="none" stroke-width="0.8"/></svg>`;
|
|
const encodedSvg = encodeURIComponent(svgContent);
|
|
|
|
const styles = `
|
|
.${containerClass.value} {
|
|
position: fixed !important;
|
|
top: 0 !important;
|
|
left: 0 !important;
|
|
width: 100vw !important;
|
|
height: 100vh !important;
|
|
pointer-events: none !important;
|
|
z-index: 999999999 !important;
|
|
overflow: hidden !important;
|
|
opacity: 0.02 !important;
|
|
background-image: url("data:image/svg+xml,${encodedSvg}") !important;
|
|
background-repeat: repeat !important;
|
|
background-size: 80px 16px !important;
|
|
background-position: 10px 10px !important;
|
|
}
|
|
`;
|
|
|
|
styleElement.textContent = styles;
|
|
|
|
Object.defineProperty(styleElement, 'remove', {
|
|
value: () => {
|
|
setTimeout(() => {
|
|
if (!styleElement?.parentNode) {
|
|
document.head.appendChild(styleElement!);
|
|
}
|
|
}, 0);
|
|
},
|
|
writable: false,
|
|
configurable: false,
|
|
});
|
|
|
|
document.head.appendChild(styleElement);
|
|
};
|
|
|
|
const ensureWatermarkExists = () => {
|
|
if (!watermarkElement || !document.body.contains(watermarkElement)) {
|
|
const newElement = document.createElement('div');
|
|
newElement.className = containerClass.value;
|
|
newElement.setAttribute('data-watermark', 'true');
|
|
|
|
Object.defineProperty(newElement, 'remove', {
|
|
value: () => {
|
|
setTimeout(() => {
|
|
if (!document.body.contains(newElement)) {
|
|
document.body.appendChild(newElement);
|
|
}
|
|
}, 0);
|
|
},
|
|
writable: false,
|
|
configurable: false,
|
|
});
|
|
|
|
const originalStyleSetter = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'style')?.set;
|
|
if (originalStyleSetter) {
|
|
Object.defineProperty(newElement, 'style', {
|
|
get: () => {
|
|
return newElement.getAttribute('style') || '';
|
|
},
|
|
set: (value) => {
|
|
return;
|
|
},
|
|
});
|
|
}
|
|
|
|
document.body.appendChild(newElement);
|
|
watermarkElement = newElement;
|
|
|
|
startAttributeObserver();
|
|
} else if (watermarkElement.className !== containerClass.value) {
|
|
watermarkElement.className = containerClass.value;
|
|
}
|
|
|
|
if (!styleElement || !document.head.contains(styleElement)) {
|
|
createStyleElement();
|
|
}
|
|
};
|
|
|
|
const startAttributeObserver = () => {
|
|
if (attributeObserver) {
|
|
attributeObserver.disconnect();
|
|
}
|
|
|
|
if (!watermarkElement) return;
|
|
|
|
attributeObserver = new MutationObserver((mutations) => {
|
|
for (const mutation of mutations) {
|
|
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
|
if (watermarkElement && watermarkElement.className !== containerClass.value) {
|
|
watermarkElement.className = containerClass.value;
|
|
}
|
|
} else if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
|
|
if (watermarkElement) {
|
|
const style = watermarkElement.getAttribute('style');
|
|
if (style && (style.includes('display') || style.includes('visibility') || style.includes('opacity'))) {
|
|
watermarkElement.removeAttribute('style');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
attributeObserver.observe(watermarkElement, {
|
|
attributes: true,
|
|
attributeOldValue: true,
|
|
});
|
|
};
|
|
|
|
const startMutationObserver = () => {
|
|
mutationObserver = new MutationObserver((mutations) => {
|
|
for (const mutation of mutations) {
|
|
if (mutation.type === 'childList') {
|
|
mutation.removedNodes.forEach((node) => {
|
|
if (node === watermarkElement || node === styleElement) {
|
|
ensureWatermarkExists();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
mutationObserver.observe(document.body, {
|
|
childList: true,
|
|
subtree: true,
|
|
});
|
|
|
|
headObserver = new MutationObserver((mutations) => {
|
|
for (const mutation of mutations) {
|
|
if (mutation.type === 'childList') {
|
|
mutation.removedNodes.forEach((node) => {
|
|
if (node === styleElement) {
|
|
ensureWatermarkExists();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
headObserver.observe(document.head, {
|
|
childList: true,
|
|
subtree: true,
|
|
});
|
|
};
|
|
|
|
const watermarkText = computed(() => $i?.id || "not signed in");
|
|
const pathData = computed(() => encodeTextToPath(watermarkText.value));
|
|
const textWidth = computed(() => watermarkText.value.length * 7 + 10);
|
|
|
|
onMounted(() => {
|
|
allCreatedClasses.push(containerClass.value);
|
|
createStyleElement();
|
|
ensureWatermarkExists();
|
|
startMutationObserver();
|
|
|
|
checkInterval = setInterval(() => {
|
|
ensureWatermarkExists();
|
|
}, 100);
|
|
|
|
regenerationInterval = setInterval(() => {
|
|
regenerateWatermark();
|
|
}, 5000);
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
if (checkInterval) {
|
|
clearInterval(checkInterval);
|
|
}
|
|
if (regenerationInterval) {
|
|
clearInterval(regenerationInterval);
|
|
}
|
|
if (mutationObserver) {
|
|
mutationObserver.disconnect();
|
|
}
|
|
if (headObserver) {
|
|
headObserver.disconnect();
|
|
}
|
|
if (attributeObserver) {
|
|
attributeObserver.disconnect();
|
|
}
|
|
cleanupOldElements();
|
|
if (styleElement && styleElement.parentNode) {
|
|
styleElement.parentNode.removeChild(styleElement);
|
|
}
|
|
if (watermarkElement && watermarkElement.parentNode) {
|
|
watermarkElement.parentNode.removeChild(watermarkElement);
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div style="display: none;" :key="`watermark-${recreationKey}`"></div>
|
|
</template>
|