514 lines
18 KiB
HTML
514 lines
18 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Analog Clock</title>
|
||
<link rel="stylesheet" href="style.css">
|
||
</head>
|
||
<body>
|
||
<div class="clock-container">
|
||
<div class="clock">
|
||
<!-- Clock face numbers -->
|
||
<div class="number number-1"><span>1</span></div>
|
||
<div class="number number-2"><span>2</span></div>
|
||
<div class="number number-3"><span>3</span></div>
|
||
<div class="number number-4"><span>4</span></div>
|
||
<div class="number number-5"><span>5</span></div>
|
||
<div class="number number-6"><span>6</span></div>
|
||
<div class="number number-7"><span>7</span></div>
|
||
<div class="number number-8"><span>8</span></div>
|
||
<div class="number number-9"><span>9</span></div>
|
||
<div class="number number-10"><span>10</span></div>
|
||
<div class="number number-11"><span>11</span></div>
|
||
<div class="number number-12"><span>12</span></div>
|
||
|
||
<!-- Clock hands -->
|
||
<div class="hand hour-hand" id="hour-hand"></div>
|
||
<div class="hand minute-hand" id="minute-hand"></div>
|
||
<div class="hand second-hand" id="second-hand"></div>
|
||
|
||
<!-- Center pivot -->
|
||
<div class="center-pivot"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="script.js"></script>
|
||
|
||
<script>
|
||
/**
|
||
* Iframe 元素高亮注入脚本
|
||
* 需要在目标网站中引入此脚本来支持跨域 iframe 高亮功能
|
||
*
|
||
* 使用方法:
|
||
* 1. 将此脚本添加到目标网站的 HTML 中
|
||
* 2. 或通过浏览器扩展、用户脚本等方式注入
|
||
*/
|
||
|
||
(function () {
|
||
"use strict";
|
||
|
||
// 检查是否在 iframe 中
|
||
if (window.self === window.top) {
|
||
return; // 不在 iframe 中,不执行
|
||
}
|
||
|
||
// 检查是否已经初始化过
|
||
if (window.__iframeHighlightInitialized) {
|
||
return;
|
||
}
|
||
window.__iframeHighlightInitialized = true;
|
||
console.log("Iframe 高亮脚本已加载");
|
||
|
||
// 创建高亮覆盖层
|
||
var overlay = document.createElement("div");
|
||
overlay.id = "iframe-highlight-overlay";
|
||
overlay.style.cssText = "\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n pointer-events: none;\n z-index: 999999;\n overflow: hidden;\n ";
|
||
|
||
// 创建悬停高亮框(虚线边框)
|
||
var highlightBox = document.createElement("div");
|
||
highlightBox.id = "iframe-highlight-box";
|
||
highlightBox.style.cssText = "\n position: absolute;\n border: 2px dashed #007AFF;\n background: rgba(0, 122, 255, 0.08);\n pointer-events: none;\n display: none;\n transition: all 0.1s ease;\n box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.8);\n border-radius: 2px;\n ";
|
||
|
||
// 创建选中节点的常驻高亮框(实线边框)
|
||
var selectedBox = document.createElement("div");
|
||
selectedBox.id = "iframe-selected-box";
|
||
selectedBox.style.cssText = "\n position: absolute;\n border: 2px solid #007AFF;\n pointer-events: none;\n display: none;\n box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.9), 0 0 8px rgba(255, 107, 53, 0.4);\n border-radius: 2px;\n z-index: 1000000;\n ";
|
||
|
||
// 创建悬停标签显示
|
||
var tagLabel = document.createElement("div");
|
||
tagLabel.id = "iframe-tag-label";
|
||
tagLabel.style.cssText = "\n position: absolute;\n background: #007AFF;\n color: white;\n padding: 2px 6px;\n font-size: 11px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n border-radius: 2px;\n pointer-events: none;\n display: none;\n white-space: nowrap;\n z-index: 1000001;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n font-weight: 500;\n ";
|
||
|
||
// 创建选中节点标签
|
||
var selectedLabel = document.createElement("div");
|
||
selectedLabel.id = "iframe-selected-label";
|
||
selectedLabel.style.cssText = "\n position: absolute;\n background: #007AFF;\n color: white;\n padding: 3px 8px;\n font-size: 11px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n border-radius: 3px;\n pointer-events: none;\n display: none;\n white-space: nowrap;\n z-index: 1000002;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);\n font-weight: 600;\n ";
|
||
overlay.appendChild(highlightBox);
|
||
overlay.appendChild(selectedBox);
|
||
overlay.appendChild(tagLabel);
|
||
overlay.appendChild(selectedLabel);
|
||
document.body.appendChild(overlay);
|
||
|
||
// 存储当前选中的元素
|
||
var selectedElement = null;
|
||
var highlightEnabled = false;
|
||
|
||
// 更新选中元素的高亮显示
|
||
function updateSelectedHighlight(element) {
|
||
console.log("updateSelectedHighlight called with:", element);
|
||
if (!element) {
|
||
selectedBox.style.display = "none";
|
||
selectedLabel.style.display = "none";
|
||
selectedElement = null;
|
||
console.log("Cleared selected highlight");
|
||
return;
|
||
}
|
||
selectedElement = element;
|
||
var rect = element.getBoundingClientRect();
|
||
console.log("Selected element rect:", rect);
|
||
|
||
// 更新选中高亮框位置
|
||
selectedBox.style.display = "block";
|
||
selectedBox.style.left = "".concat(rect.left - 2, "px");
|
||
selectedBox.style.top = "".concat(rect.top - 2, "px");
|
||
selectedBox.style.width = "".concat(rect.width + 4, "px");
|
||
selectedBox.style.height = "".concat(rect.height + 4, "px");
|
||
|
||
// 更新选中标签位置和内容
|
||
selectedLabel.style.display = "block";
|
||
selectedLabel.textContent = "\u2713 <".concat(element.tagName.toLowerCase(), ">");
|
||
|
||
// 计算标签位置,确保不超出视窗
|
||
var labelTop = rect.top - 28;
|
||
var labelLeft = rect.left;
|
||
|
||
// 如果标签会超出顶部,显示在元素下方
|
||
if (labelTop < 5) {
|
||
labelTop = rect.bottom + 5;
|
||
}
|
||
|
||
// 如果标签会超出右侧,向左调整
|
||
var labelWidth = selectedLabel.offsetWidth || 100; // 预估宽度
|
||
if (labelLeft + labelWidth > window.innerWidth - 10) {
|
||
labelLeft = window.innerWidth - labelWidth - 10;
|
||
}
|
||
selectedLabel.style.left = "".concat(Math.max(5, labelLeft), "px");
|
||
selectedLabel.style.top = "".concat(labelTop, "px");
|
||
console.log("Selected highlight positioned at:", {
|
||
left: selectedBox.style.left,
|
||
top: selectedBox.style.top,
|
||
width: selectedBox.style.width,
|
||
height: selectedBox.style.height
|
||
});
|
||
}
|
||
function getElementSelector(element) {
|
||
if (!(element instanceof Element)) throw new Error('Argument must be a DOM element');
|
||
var segments = [];
|
||
var current = element;
|
||
while (current !== document.documentElement) {
|
||
var selector = '';
|
||
// 优先检查唯一ID
|
||
if (current.id && document.querySelectorAll("#".concat(current.id)).length === 1) {
|
||
segments.unshift("#".concat(current.id));
|
||
break; // ID唯一,无需继续向上
|
||
}
|
||
|
||
// 生成类名选择器(取第一个有效类名)
|
||
var classes = Array.from(current.classList).filter(function (c) {
|
||
return !c.startsWith('js-');
|
||
});
|
||
var className = classes.length > 0 ? ".".concat(classes[0]) : '';
|
||
|
||
// 生成位置索引(nth-child)
|
||
var tag = current.tagName.toLowerCase();
|
||
if (!className) {
|
||
var siblings = Array.from(current.parentNode.children);
|
||
var index = siblings.findIndex(function (el) {
|
||
return el === current;
|
||
}) + 1;
|
||
selector = "".concat(tag, ":nth-child(").concat(index, ")");
|
||
} else {
|
||
selector = className;
|
||
}
|
||
segments.unshift(selector);
|
||
current = current.parentElement;
|
||
}
|
||
|
||
// 处理根元素
|
||
if (current === document.documentElement) {
|
||
segments.unshift('html');
|
||
}
|
||
return segments.join(' > ');
|
||
}
|
||
|
||
// 获取元素文本内容
|
||
function getElementText(element) {
|
||
var _element$textContent;
|
||
if (element.tagName === "INPUT") {
|
||
return element.value || element.placeholder || "";
|
||
}
|
||
if (element.tagName === "TEXTAREA") {
|
||
return element.value || element.placeholder || "";
|
||
}
|
||
var text = ((_element$textContent = element.textContent) === null || _element$textContent === void 0 ? void 0 : _element$textContent.trim()) || "";
|
||
return text.length > 50 ? text.substring(0, 50) + "..." : text;
|
||
}
|
||
|
||
// 获取元素属性信息
|
||
function getElementAttributes(element) {
|
||
var attrs = {};
|
||
for (var i = 0; i < element.attributes.length; i++) {
|
||
var attr = element.attributes[i];
|
||
attrs[attr.name] = attr.value;
|
||
}
|
||
return attrs;
|
||
}
|
||
|
||
// 鼠标悬停事件处理
|
||
function handleMouseOver(e) {
|
||
if (!highlightEnabled) return;
|
||
var target = e.target;
|
||
if (!target || target === overlay || target === highlightBox || target === tagLabel || target === selectedBox || target === selectedLabel) {
|
||
return;
|
||
}
|
||
|
||
// 避免高亮 html 和 body 元素
|
||
if (target === document.documentElement || target === document.body) {
|
||
return;
|
||
}
|
||
|
||
// 如果是已选中的元素,不显示悬停高亮
|
||
if (target === selectedElement) {
|
||
highlightBox.style.display = "none";
|
||
tagLabel.style.display = "none";
|
||
return;
|
||
}
|
||
var rect = target.getBoundingClientRect();
|
||
var selector = getElementSelector(target);
|
||
var text = getElementText(target);
|
||
var attributes = getElementAttributes(target);
|
||
|
||
// 更新悬停高亮框位置
|
||
highlightBox.style.display = "block";
|
||
highlightBox.style.left = "".concat(rect.left - 2, "px");
|
||
highlightBox.style.top = "".concat(rect.top - 2, "px");
|
||
highlightBox.style.width = "".concat(rect.width + 4, "px");
|
||
highlightBox.style.height = "".concat(rect.height + 4, "px");
|
||
|
||
// 更新标签位置和内容
|
||
tagLabel.style.display = "block";
|
||
tagLabel.textContent = "<".concat(target.tagName.toLowerCase(), ">");
|
||
|
||
// 计算标签位置,确保不超出视窗
|
||
var labelTop = rect.top - 22;
|
||
var labelLeft = rect.left;
|
||
|
||
// 如果标签会超出顶部,显示在元素下方
|
||
if (labelTop < 0) {
|
||
labelTop = rect.bottom + 5;
|
||
}
|
||
|
||
// 如果标签会超出右侧,向左调整
|
||
if (labelLeft + tagLabel.offsetWidth > window.innerWidth) {
|
||
labelLeft = window.innerWidth - tagLabel.offsetWidth - 5;
|
||
}
|
||
tagLabel.style.left = "".concat(Math.max(0, labelLeft), "px");
|
||
tagLabel.style.top = "".concat(labelTop, "px");
|
||
|
||
// 发送消息到父窗口
|
||
var elementInfo = {
|
||
tagName: target.tagName.toLowerCase(),
|
||
rect: {
|
||
left: rect.left,
|
||
top: rect.top,
|
||
right: rect.right,
|
||
bottom: rect.bottom,
|
||
width: rect.width,
|
||
height: rect.height,
|
||
x: rect.x,
|
||
y: rect.y
|
||
},
|
||
selector: selector,
|
||
text: text,
|
||
attributes: attributes,
|
||
url: window.location.href,
|
||
path: window.location.pathname,
|
||
timestamp: Date.now()
|
||
};
|
||
try {
|
||
window.parent.postMessage({
|
||
type: "iframe-element-hover",
|
||
data: elementInfo,
|
||
source: "iframe-highlight-injector"
|
||
}, "*");
|
||
} catch (error) {
|
||
console.warn("无法发送消息到父窗口:", error);
|
||
}
|
||
}
|
||
|
||
// 鼠标离开事件处理
|
||
function handleMouseOut(e) {
|
||
if (!highlightEnabled) return;
|
||
var relatedTarget = e.relatedTarget;
|
||
|
||
// 如果鼠标移动到高亮相关元素上,不隐藏高亮
|
||
if (relatedTarget && (relatedTarget === highlightBox || relatedTarget === tagLabel || relatedTarget === overlay || relatedTarget === selectedBox || relatedTarget === selectedLabel)) {
|
||
return;
|
||
}
|
||
highlightBox.style.display = "none";
|
||
tagLabel.style.display = "none";
|
||
try {
|
||
window.parent.postMessage({
|
||
type: "iframe-element-hover",
|
||
data: null,
|
||
source: "iframe-highlight-injector"
|
||
}, "*");
|
||
} catch (error) {
|
||
console.warn("无法发送消息到父窗口:", error);
|
||
}
|
||
}
|
||
|
||
// 点击事件处理
|
||
function handleClick(e) {
|
||
var target = e.target;
|
||
if (!target || target === overlay || target === highlightBox || target === tagLabel || target === selectedBox || target === selectedLabel) {
|
||
return;
|
||
}
|
||
|
||
// 避免处理 html 和 body 元素
|
||
if (target === document.documentElement || target === document.body) {
|
||
return;
|
||
}
|
||
|
||
// 检查是否是交互元素,这些元素需要保留默认行为
|
||
var isInteractiveElement = ['input', 'textarea', 'select', 'button', 'a'].includes(target.tagName.toLowerCase());
|
||
|
||
// 如果高亮功能启用,对于非交互元素阻止默认行为和事件传播
|
||
if (highlightEnabled) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
}
|
||
var rect = target.getBoundingClientRect();
|
||
var selector = getElementSelector(target);
|
||
var text = getElementText(target);
|
||
var attributes = getElementAttributes(target);
|
||
console.log("Element clicked:", {
|
||
tagName: target.tagName,
|
||
selector: selector,
|
||
rect: rect
|
||
});
|
||
|
||
// 立即更新选中高亮
|
||
updateSelectedHighlight(target);
|
||
|
||
// 隐藏悬停高亮,因为现在是选中状态
|
||
highlightBox.style.display = "none";
|
||
tagLabel.style.display = "none";
|
||
var elementInfo = {
|
||
tagName: target.tagName.toLowerCase(),
|
||
rect: {
|
||
left: rect.left,
|
||
top: rect.top,
|
||
right: rect.right,
|
||
bottom: rect.bottom,
|
||
width: rect.width,
|
||
height: rect.height,
|
||
x: rect.x,
|
||
y: rect.y
|
||
},
|
||
selector: selector,
|
||
text: text,
|
||
attributes: attributes,
|
||
url: window.location.href,
|
||
path: window.location.pathname,
|
||
timestamp: Date.now()
|
||
};
|
||
try {
|
||
window.parent.postMessage({
|
||
type: "iframe-element-click",
|
||
data: elementInfo,
|
||
source: "iframe-highlight-injector"
|
||
}, "*");
|
||
} catch (error) {
|
||
console.warn("无法发送消息到父窗口:", error);
|
||
}
|
||
}
|
||
|
||
// 监听来自父窗口的消息
|
||
function handleParentMessage(event) {
|
||
console.log("Received message from parent:", event.data);
|
||
if (event.data.type === "iframe-highlight-toggle") {
|
||
var enabled = event.data.enabled;
|
||
console.log("Highlight toggle:", enabled);
|
||
if (enabled) {
|
||
enableHighlight();
|
||
} else {
|
||
disableHighlight();
|
||
}
|
||
} else if (event.data.type === "enable-iframe-highlight") {
|
||
console.log("Enable iframe highlight");
|
||
enableHighlight();
|
||
} else if (event.data.type === "disable-iframe-highlight") {
|
||
console.log("Disable iframe highlight");
|
||
disableHighlight();
|
||
} else if (event.data.type === "toggle-iframe-highlight") {
|
||
var _enabled = event.data.enabled !== undefined ? event.data.enabled : !highlightEnabled;
|
||
console.log("Toggle iframe highlight to:", _enabled);
|
||
if (_enabled) {
|
||
enableHighlight();
|
||
} else {
|
||
disableHighlight();
|
||
}
|
||
} else if (event.data.type === "update-selected-element") {
|
||
var selector = event.data.selector;
|
||
console.log("Update selected element with selector:", selector);
|
||
if (selector) {
|
||
try {
|
||
var element = document.querySelector(selector);
|
||
console.log("Found element by selector:", element);
|
||
updateSelectedHighlight(element);
|
||
} catch (error) {
|
||
console.warn("Failed to select element:", error);
|
||
updateSelectedHighlight(null);
|
||
}
|
||
} else {
|
||
updateSelectedHighlight(null);
|
||
}
|
||
} else if (event.data.type === "clear-selected-element") {
|
||
console.log("Clear selected element");
|
||
updateSelectedHighlight(null);
|
||
}
|
||
}
|
||
|
||
// 启用高亮功能
|
||
function enableHighlight() {
|
||
console.log("Enabling highlight");
|
||
document.addEventListener("mouseover", handleMouseOver, true);
|
||
document.addEventListener("mouseout", handleMouseOut, true);
|
||
document.addEventListener("click", handleClick, true);
|
||
highlightEnabled = true;
|
||
overlay.style.display = "block";
|
||
}
|
||
|
||
// 禁用高亮功能
|
||
function disableHighlight() {
|
||
console.log("Disabling highlight");
|
||
highlightEnabled = false;
|
||
// 保持事件监听器,但通过 highlightEnabled 变量控制行为
|
||
// 这样可以保留选中状态的显示
|
||
highlightBox.style.display = "none";
|
||
tagLabel.style.display = "none";
|
||
// 不隐藏 selectedBox 和 selectedLabel,保留选中状态
|
||
}
|
||
|
||
// 完全禁用高亮功能(移除所有监听器)
|
||
function fullyDisableHighlight() {
|
||
console.log("Fully disabling highlight");
|
||
highlightEnabled = false;
|
||
document.removeEventListener("mouseover", handleMouseOver, true);
|
||
document.removeEventListener("mouseout", handleMouseOut, true);
|
||
document.removeEventListener("click", handleClick, true);
|
||
overlay.style.display = "none";
|
||
highlightBox.style.display = "none";
|
||
tagLabel.style.display = "none";
|
||
selectedBox.style.display = "none";
|
||
selectedLabel.style.display = "none";
|
||
}
|
||
|
||
// 添加事件监听
|
||
enableHighlight();
|
||
window.addEventListener("message", handleParentMessage);
|
||
|
||
// 暴露全局函数供外部调用
|
||
window.__iframeHighlightControl = {
|
||
enable: enableHighlight,
|
||
disable: disableHighlight,
|
||
fullyDisable: fullyDisableHighlight,
|
||
isEnabled: function isEnabled() {
|
||
return highlightEnabled;
|
||
},
|
||
getSelectedElement: function getSelectedElement() {
|
||
return selectedElement;
|
||
},
|
||
updateSelected: updateSelectedHighlight,
|
||
// 通过消息发送开关控制
|
||
sendToggleMessage: function sendToggleMessage(enabled) {
|
||
window.parent.postMessage({
|
||
type: 'iframe-highlight-status',
|
||
enabled: enabled || highlightEnabled,
|
||
source: 'iframe-highlight-injector'
|
||
}, '*');
|
||
}
|
||
};
|
||
|
||
// 通知父窗口脚本已加载
|
||
try {
|
||
window.parent.postMessage({
|
||
type: "iframe-highlight-ready",
|
||
data: {
|
||
url: window.location.href,
|
||
userAgent: navigator.userAgent,
|
||
timestamp: Date.now()
|
||
},
|
||
source: "iframe-highlight-injector"
|
||
}, "*");
|
||
} catch (error) {
|
||
console.warn("无法发送就绪消息到父窗口:", error);
|
||
}
|
||
|
||
// 清理函数
|
||
window.__iframeHighlightCleanup = function () {
|
||
fullyDisableHighlight();
|
||
window.removeEventListener("message", handleParentMessage);
|
||
if (overlay.parentElement) {
|
||
overlay.parentElement.removeChild(overlay);
|
||
}
|
||
delete window.__iframeHighlightInitialized;
|
||
delete window.__iframeHighlightCleanup;
|
||
};
|
||
})();
|
||
|
||
</script>
|
||
</body>
|
||
</html> |