123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- /**
- * 侧边栏管理类
- */
- class SidebarManager {
- /**
- * @constant {Object} 配置常量
- */
- static CONFIG = {
- SIDEBAR_ID: "paiwise-sidebar",
- WRAPPER_ID: "paiwise-main-wrapper",
- STORAGE_KEY: "sidebarOpen",
- };
- constructor() {
- this.sidebarId = SidebarManager.CONFIG.SIDEBAR_ID;
- this.wrapperId = SidebarManager.CONFIG.WRAPPER_ID;
- this.isOpen = false;
- this.currentUrl = window.location.href;
- this.currentTitle = document.title;
- this.init();
- }
- /**
- * 初始化侧边栏
- */
- async init() {
- try {
- await this.checkAndRestoreState();
- this.setupEventListeners();
- } catch (error) {
- console.error("Sidebar initialization failed:", error);
- }
- }
- /**
- * 设置事件监听器
- */
- setupEventListeners() {
- // 监听页面加载完成事件
- window.addEventListener("load", () => this.checkAndRestoreState());
- // 监听来自 sidebar 的消息
- window.addEventListener("message", this.handleMessage.bind(this));
- // 监听窗口大小变化
- window.addEventListener(
- "resize",
- Utils.debounce(() => this.handleResize(), 100)
- );
- // 监听页面可见性变化
- document.addEventListener("visibilitychange", () => {
- if (!document.hidden) {
- this.checkAndRestoreState();
- }
- });
- // 监听 history 变化
- window.addEventListener("popstate", () => {
- this.checkAndRestoreState();
- });
- // 监听标题变化
- const titleObserver = new MutationObserver(() => {
- if (document.title !== this.currentTitle) {
- this.currentTitle = document.title;
- this.updatePageInfo();
- }
- });
- titleObserver.observe(
- document.querySelector("head > title") || document.head,
- {
- subtree: true,
- characterData: true,
- childList: true,
- }
- );
- // 监听页面 URL 变化
- new MutationObserver(() => {
- const url = location.href;
- if (url !== this.currentUrl) {
- this.currentUrl = url;
- this.updatePageInfo();
- this.checkAndRestoreState();
- }
- }).observe(document, { subtree: true, childList: true });
- // 监听来自sidebar的消息
- window.addEventListener("message", async (event) => {
- if (event.data.type === "COPY_TO_CLIPBOARD") {
- try {
- await navigator.clipboard.writeText(event.data.text);
- // 可选:发送成功消息回sidebar
- event.source.postMessage(
- {
- type: "COPY_SUCCESS",
- },
- "*"
- );
- } catch (err) {
- console.error("Failed to copy text:", err);
- // 可选:发送失败消息回sidebar
- event.source.postMessage(
- {
- type: "COPY_ERROR",
- error: err.message,
- },
- "*"
- );
- }
- }
- if (event.data.type === "FILL_INPUT") {
- inputs.find(input => input.placeholder.includes('账号')).value = event.data.data[1][0]
- inputs.find(input => input.placeholder.includes('密码')).value = event.data.data[1][1]
- }
- });
- // 监听来自iframe的消息
- window.addEventListener("message", (event) => {
- // 确保消息来自我们的iframe
- if (
- event.source ===
- document.getElementById("paiwise-sidebar")?.contentWindow
- ) {
- if (event.data.type === "FILL_INPUT") {
- event.source.postMessage(
- {
- type: "GE_T",
- pageInfo: pageInfo,
- },
- "*"
- );
- }
- if (event.data.type === "ANALYZE_PAGE") {
- // 分析页面并返回结果
- const pageInfo = window.pageAnalyzer.analyzePage();
- // 发送分析结果回iframe
- event.source.postMessage(
- {
- type: "PAGE_ANALYSIS_RESULT",
- pageInfo: pageInfo,
- },
- "*"
- );
- }
- }
- });
- }
- /**
- * 处理接收到的消息
- * @param {MessageEvent} event
- */
- handleMessage(event) {
- if (event.data.action === "closeSidebar") {
- this.removeSidebar();
- }
- }
- /**
- * 处理窗口大小变化
- */
- handleResize() {
- const sidebar = document.getElementById(this.sidebarId);
- if (sidebar && this.isOpen) {
- sidebar.style.height = `${window.innerHeight}px`;
- }
- }
- /**
- * 检查并恢复侧边栏状态
- */
- async checkAndRestoreState() {
- try {
- const isOpen = await Utils.getStorageData(
- SidebarManager.CONFIG.STORAGE_KEY
- );
- if (isOpen && !this.isOpen) {
- // 只有当应该打开且当前未打开时才创建
- this.createSidebar();
- } else if (!isOpen && this.isOpen) {
- // 只有当应该关闭且当前打开时才移除
- this.removeSidebar();
- }
- } catch (error) {
- console.error("Failed to restore sidebar state:", error);
- }
- }
- /**
- * 包装页面内容
- */
- wrapPageContent() {
- if (document.getElementById(this.wrapperId)) return;
- const wrapper = document.createElement("div");
- wrapper.id = this.wrapperId;
- // 保存body的原始样式
- this.originalBodyStyle = {
- width: document.body.style.width,
- margin: document.body.style.margin,
- position: document.body.style.position,
- overflow: document.body.style.overflow,
- };
- // 包装内容
- while (document.body.firstChild) {
- if (document.body.firstChild.id !== this.sidebarId) {
- wrapper.appendChild(document.body.firstChild);
- } else {
- document.body.removeChild(document.body.firstChild);
- }
- }
- document.body.appendChild(wrapper);
- }
- /**
- * 解除页面内容包装
- */
- unwrapPageContent() {
- const wrapper = document.getElementById(this.wrapperId);
- if (!wrapper) return;
- // 恢复body的原始样式
- if (this.originalBodyStyle) {
- Object.assign(document.body.style, this.originalBodyStyle);
- }
- while (wrapper.firstChild) {
- document.body.insertBefore(wrapper.firstChild, wrapper);
- }
- wrapper.remove();
- }
- /**
- * 发送页面信息到iframe
- */
- sendPageInfo() {
- this.updatePageInfo();
- }
- /**
- * 更新页面信息
- */
- updatePageInfo() {
- const iframe = document.getElementById(this.sidebarId);
- if (!iframe) return;
- // 获取最新的favicon
- const favicon = this.getFavicon();
- // 发送更新后的页面信息
- iframe.contentWindow.postMessage(
- {
- type: "PAGE_INFO",
- pageInfo: {
- favicon,
- title: document.title,
- url: window.location.href,
- },
- },
- "*"
- );
- }
- /**
- * 获取最新的favicon
- */
- getFavicon() {
- // 尝试获取动态favicon
- const iconLinks = Array.from(
- document.querySelectorAll('link[rel*="icon"]')
- );
- const favicon = iconLinks
- .sort((a, b) => {
- // 优先使用大尺寸图标
- const sizeA = parseInt(a.sizes?.value) || 0;
- const sizeB = parseInt(b.sizes?.value) || 0;
- return sizeB - sizeA;
- })
- .map((link) => link.href)
- .find(Boolean);
- if (favicon) return favicon;
- // 如果没有找到,返回网站根目录的favicon.ico
- const url = new URL(window.location.href);
- return `${url.protocol}//${url.hostname}/favicon.ico`;
- }
- /**
- * 创建侧边栏
- */
- async createSidebar() {
- if (document.getElementById(this.sidebarId)) return;
- try {
- this.wrapPageContent();
- const iframe = document.createElement("iframe");
- iframe.id = this.sidebarId;
- iframe.src = chrome.runtime.getURL("sidebar.html");
- // 先添加到DOM,但不添加show类
- document.body.appendChild(iframe);
- // 等待iframe加载完成
- await new Promise((resolve) => {
- iframe.onload = () => {
- // 发送页面信息
- this.sendPageInfo();
- resolve();
- };
- });
- // 等待一帧以确保DOM更新
- await new Promise((resolve) => requestAnimationFrame(resolve));
- // 触发动画
- document.body.classList.add("sidebar-open");
- iframe.classList.add("show");
- // 等待动画完成
- await new Promise((resolve) => {
- iframe.addEventListener("transitionend", resolve, { once: true });
- });
- this.isOpen = true;
- await Utils.setStorageData(SidebarManager.CONFIG.STORAGE_KEY, true);
- } catch (error) {
- console.error("Failed to create sidebar:", error);
- this.unwrapPageContent();
- }
- }
- /**
- * 移除侧边栏
- */
- async removeSidebar() {
- if (!this.isOpen) return;
- try {
- const sidebar = document.getElementById(this.sidebarId);
- if (sidebar) {
- document.body.classList.remove("sidebar-open");
- sidebar.classList.remove("show");
- // 等待动画完成后再移除元素
- await new Promise((resolve) => {
- sidebar.addEventListener(
- "transitionend",
- () => {
- this.unwrapPageContent();
- sidebar.remove();
- resolve();
- },
- { once: true }
- );
- });
- }
- this.isOpen = false;
- await Utils.setStorageData(SidebarManager.CONFIG.STORAGE_KEY, false);
- } catch (error) {
- console.error("Failed to remove sidebar:", error);
- }
- }
- /**
- * 切换侧边栏显示状态
- */
- toggle() {
- if (this.isOpen) {
- this.removeSidebar();
- } else {
- this.createSidebar();
- }
- }
- }
- // 初始化侧边栏管理器
- const sidebarManager = new SidebarManager();
- // 监听来自背景脚本的消息
- chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
- if (message.action === "toggleSidebar") {
- sidebarManager.toggle();
- sendResponse({ success: true });
- }
- sendResponse({ success: true });
- });
- window.onload = () => {
- inputs = [...document.querySelectorAll('input')]
- }
- let inputs = []
- // 创建一个新的js文件用于处理侧边栏内部的关闭按钮事件
|