cela-auto

中国干部网络学院自动学习脚本,支持浦东分院等多种环境,采用状态机驱动的极简高效架构。模块化重构版本。

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(У мене вже є менеджер скриптів, дайте мені встановити його!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         cela-auto
// @namespace    https://github.com/Moker32/
// @version      4.1.0
// @description  中国干部网络学院自动学习脚本,支持浦东分院等多种环境,采用状态机驱动的极简高效架构。模块化重构版本。
// @author       Moker32
// @license      GPL-3.0-or-later
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @match        *://cela.e-celap.cn/*
// @match        *://pudong.e-celap.cn/*
// @match        *://pd.cela.cn/*
// @match        *://*.e-celap.cn/*
// @match        *://www.cela.gov.cn/*
// @match        *://cela.gwypx.com.cn/*
// @match        *://cela.cbead.cn/*
// @connect      cela.e-celap.cn
// @connect      pudong.e-celap.cn
// @connect      pd.cela.cn
// @connect      cela.gwypx.com.cn
// @connect      cela.cbead.cn
// @connect      www.cela.gov.cn
// @connect      zpyapi.shsets.com
// @run-at       document-idle
// ==/UserScript==
(function(exports) {
    "use strict";
    const CONSTANTS = {
        EVENTS: {
            LOG: "log",
            STATUS_UPDATE: "statusUpdate",
            PROGRESS_UPDATE: "progressUpdate",
            STATISTICS_UPDATE: "statisticsUpdate",
            LEARNING_START: "learningStart",
            LEARNING_STOP: "learningStop",
            COURSE_START: "courseStart",
            COURSE_COMPLETE: "courseComplete",
            COURSE_SKIP: "courseSkip",
            COURSE_ERROR: "courseError",
            PROGRESS_REPORT: "progressReport",
            PROGRESS_SUCCESS: "progressSuccess",
            PROGRESS_ERROR: "progressError",
            ERROR: "error",
            FATAL_ERROR: "fatalError"
        },
        LEARNING_STATES: {
            IDLE: "idle",
            PREPARING: "preparing",
            LEARNING: "learning",
            COMPLETED: "completed",
            FAILED: "failed",
            SKIPPED: "skipped"
        },
        FLOW_RESULTS: {
            WAITING_FOR_USER: "WAITING_FOR_USER"
        },
        COMPLETION_THRESHOLD: 80,
        SKIP_COMPLETED_COURSES: true,
        API_ENDPOINTS: {
            GET_PLAY_TREND: "/inc/nc/course/play/getPlayTrend",
            PULSE_SAVE_RECORD: "/inc/nc/course/play/pulseSaveRecord",
            GET_STUDY_RECORD: "/inc/nc/course/getStudyRecord",
            GET_COURSEWARE_DETAIL: "/inc/nc/course/play/getCoursewareDetail",
            GET_PACK_BY_ID: "/inc/nc/pack/getById",
            GET_COURSE_LIST: "/api/course/list"
        },
        SELECTORS: {
            PANEL: "#api-learner-panel",
            STATUS_DISPLAY: "#learner-status",
            PROGRESS_INNER: "#learner-progress-inner",
            TOGGLE_BTN: "#toggle-learning-btn",
            LOG_CONTAINER: "#api-learner-panel .log-container",
            STAT_TOTAL: "#stat-total",
            STAT_COMPLETED: "#stat-completed",
            STAT_LEARNED: "#stat-learned",
            STAT_FAILED: "#stat-failed",
            STAT_SKIPPED: "#stat-skipped",
            APP: "#app"
        },
        STORAGE_KEYS: {
            TOKEN: "token",
            AUTH_TOKEN: "authToken",
            ACCESS_TOKEN: "access_token",
            USER_ID: "userId",
            USER_ID_ALT: "user_id"
        },
        COURSE_SELECTORS: [ ".dsf-many-schedule-course-list-row", ".dsf_nc_pd_special_item", '[class*="course"]', "[data-course]", ".course-item", ".lesson-item", ".el-card", ".el-card__body", ".course-card", ".course-box", ".nc-course-item", ".study-item", ".learn-item", '[class*="item"]', '[class*="card"]', "[data-id]", ".pudong-course", ".pd-course", ".dsf-course", ".dsjy_card", ".item_content", ".class-item-desc" ],
        VIDEO_SELECTORS: [ "video", "[api-base-url]", '[class*="video"]', 'iframe[src*="play"]' ],
        COOKIE_PATTERNS: {
            USER_ID: /userId=([^;]+)/,
            TOKEN: /token=([^;]+)/,
            P_PARAM: /_p=([^;]+)/
        },
        TIME_FORMATS: {
            DEFAULT_DURATION: 1800
        },
        UI_LIMITS: {
            MAX_LOG_ENTRIES: 50,
            LOG_FLUSH_DELAY: 100
        },
        ENVIRONMENTS: {
            PORTAL: {
                name: "中央门户",
                hostnames: [ "www.cela.gov.cn" ],
                pathPatterns: [ "/home" ],
                features: {
                    type: "traditional",
                    framework: "jquery",
                    courseContainer: "#courseCon",
                    branchNavigation: "#branchCon"
                },
                supported: false,
                reason: "门户网站仅用于信息展示,不支持自动学习"
            },
            PUDONG: {
                name: "浦东分院",
                hostnames: [ "pudong.e-celap.cn", "pd.cela.cn", "cela.e-celap.cn" ],
                pathPatterns: [ "/pc/nc/page", "/page:pd" ],
                features: {
                    type: "spa",
                    framework: "vue",
                    router: "hash",
                    apiBase: "auto",
                    ssoParam: "cela_sso_logged"
                },
                supported: true,
                variants: {
                    MAIN: "cela.e-celap.cn",
                    SUBDOMAIN: "pudong.e-celap.cn",
                    ALIAS: "pd.cela.cn"
                }
            },
            GWYPX: {
                name: "党校分院",
                hostnames: [ "cela.gwypx.com.cn" ],
                supported: false,
                reason: "暂不支持党校分院环境"
            },
            CBEAD: {
                name: "企业分院",
                hostnames: [ "cela.cbead.cn" ],
                pathPatterns: [ "/home", "/study", "/train-new" ],
                features: {
                    type: "spa",
                    framework: "vue",
                    router: "hash",
                    apiBase: "auto",
                    ssoParam: "cela_sso_logged"
                },
                supported: true,
                variants: {
                    MAIN: "cela.cbead.cn"
                }
            }
        },
        SELECTOR_STRATEGY: {
            PUDONG: {
                INDEX: {
                    primary: ".dsf_nc_pd_special_item",
                    secondary: ".dsjy_card",
                    tertiary: ".dsf-many-schedule-course-list-row",
                    fallback: '[class*="course"][data-id]',
                    context: "#app",
                    extractors: {
                        courseId: [ "data-id", "id", "data-course-id" ],
                        courseName: [ ".title", ".name", ".item_content", "h3", "h4" ],
                        link: [ "data-url", "href", "[data-link]" ]
                    }
                },
                COLUMN: {
                    specialdetail: {
                        primary: ".dsf_nc_pd_special_item",
                        apiEndpoint: "/inc/nc/course/pd/getPackById"
                    },
                    specialcolumn: {
                        primary: ".dsjy_card",
                        secondary: ".dsf-many-schedule-course-list-row",
                        apiEndpoint: "/inc/nc/course/pd/getPackById"
                    },
                    channelDetail: {
                        primary: ".dsf_nc_pd_special_item",
                        apiEndpoint: "/inc/nc/channel/getCourseList"
                    },
                    pdzq: {
                        primary: ".dsf_nc_pd_special_item",
                        apiEndpoint: "/inc/nc/course/pd/getPackById"
                    },
                    xisijuan: {
                        primary: ".dsf_nc_pd_special_item",
                        apiEndpoint: "/inc/nc/course/pd/getPackById"
                    }
                },
                PLAYER: {
                    container: "#coursePlayer",
                    video: 'video[api-base-url], iframe[src*="play"]',
                    controls: ".video-controls, .dsf-video-player"
                }
            },
            CBEAD: {
                INDEX: {
                    primary: ".activity-list .list-item",
                    secondary: ".course-item",
                    fallback: '[class*="course"]',
                    context: "#app",
                    extractors: {
                        courseId: [ "data-id", "id" ],
                        courseName: [ ".title", ".name" ],
                        link: [ "data-url", "href" ]
                    }
                },
                COLUMN: {
                    trainNew: {
                        primary: ".activity-stage ul.list",
                        courseItem: ".activity-stage ul.list li.clearfix",
                        extractors: {
                            titleElement: ".common-title.text-overflow",
                            titleIdPattern: /D75itemDetail1-([a-f0-9-]+)/,
                            studyBtn: ".study-btn",
                            studyBtnIdPattern: /D75itemDetail2-([a-f0-9-]+)/,
                            progressElement: ".completed-rate-bar .bar",
                            progressAttr: "style",
                            progressPattern: /width:\s*(\d+)%/
                        },
                        apiEndpoint: "/inc/nc/course/pd/getPackById"
                    }
                },
                PLAYER: {
                    catalog: ".course-side-catalog",
                    chapterList: ".chapter-list-box",
                    extractors: {
                        chapterTitle: ".chapter-item .text-overflow",
                        durationElement: ".section-item .item:last-child",
                        durationPattern: /(\d+):(\d+)/,
                        completedText: ".item-completed",
                        completedPattern: /完成率[::]\s*(\d+)%/
                    },
                    videoPlayer: "video.vjs-tech",
                    videoId: "D249player_html5_api",
                    playerType: "videojs"
                }
            },
            FALLBACK: {
                INDEX: {
                    primary: ".dsf_nc_pd_special_item",
                    secondary: ".dsjy_card",
                    tertiary: ".dsf-many-schedule-course-list-row",
                    fallback: '[class*="course"][data-id]',
                    context: "#app",
                    extractors: {
                        courseId: [ "data-id", "id", "data-course-id" ],
                        courseName: [ ".title", ".name", ".item_content", "h3", "h4" ],
                        link: [ "data-url", "href", "[data-link]" ]
                    }
                },
                COLUMN: {
                    specialdetail: {
                        primary: ".dsf_nc_pd_special_item",
                        apiEndpoint: "/inc/nc/course/pd/getPackById"
                    },
                    specialcolumn: {
                        primary: ".dsjy_card",
                        secondary: ".dsf-many-schedule-course-list-row",
                        apiEndpoint: "/inc/nc/course/pd/getPackById"
                    },
                    channelDetail: {
                        primary: ".dsf_nc_pd_special_item",
                        apiEndpoint: "/inc/nc/channel/getCourseList"
                    },
                    pdzq: {
                        primary: ".dsf_nc_pd_special_item",
                        apiEndpoint: "/inc/nc/course/pd/getPackById"
                    },
                    xisijuan: {
                        primary: ".dsf_nc_pd_special_item",
                        apiEndpoint: "/inc/nc/course/pd/getPackById"
                    }
                },
                PLAYER: {
                    container: "#coursePlayer",
                    video: 'video[api-base-url], iframe[src*="play"]',
                    controls: ".video-controls, .dsf-video-player"
                }
            }
        },
        PAGE_TYPES: {
            PORTAL_INDEX: {
                name: "门户首页",
                urlPattern: /^https?:\/\/www\.cela\.gov\.cn\/?(#.*)?$/,
                domPattern: [ "#branchCon", "#courseCon" ],
                features: [ "traditional", "jquery" ],
                actions: [ "navigate_to_branch" ]
            },
            PUDONG_INDEX: {
                name: "浦东首页",
                urlPattern: /pagehome\/index/,
                domPattern: [ "#app" ],
                features: [ "vue", "spa" ],
                actions: [ "scan_courses" ]
            },
            PUDONG_COLUMN_SPECIALDETAIL: {
                name: "浦东专题详情页",
                urlPattern: /specialdetail/,
                hashPattern: /[?&]id=([^&]+)/,
                domPattern: [ ".dsf_nc_pd_special_item" ],
                apiEndpoint: "/inc/nc/course/pd/getPackById",
                actions: [ "extract_from_api", "extract_from_dom" ]
            },
            PUDONG_COLUMN_SPECIALCOLUMN: {
                name: "浦东专栏页",
                urlPattern: /specialcolumn/,
                domPattern: [ ".dsjy_card" ],
                apiEndpoint: "/inc/nc/course/pd/getPackById",
                actions: [ "extract_from_api", "extract_from_dom" ]
            },
            PUDONG_COLUMN_CHANNELDETAIL: {
                name: "浦东频道详情页",
                urlPattern: /channelDetail/,
                domPattern: [ ".dsf_nc_pd_special_item" ],
                apiEndpoint: "/inc/nc/channel/getCourseList",
                actions: [ "extract_from_api", "extract_from_dom" ]
            },
            PUDONG_COLUMN_PDZQ: {
                name: "浦东专区页",
                urlPattern: /pdzq/,
                domPattern: [ ".dsf_nc_pd_special_item" ],
                apiEndpoint: "/inc/nc/course/pd/getPackById",
                actions: [ "extract_from_api", "extract_from_dom" ]
            },
            PUDONG_COLUMN_XISIJUAN: {
                name: "浦东西席卷页",
                urlPattern: /xisijuan/,
                domPattern: [ ".dsf_nc_pd_special_item" ],
                apiEndpoint: "/inc/nc/course/pd/getPackById",
                actions: [ "extract_from_api", "extract_from_dom" ]
            },
            PUDONG_PLAYER: {
                name: "浦东播放页",
                urlPattern: /coursePlayer/,
                domPattern: [ "#coursePlayer", "video[api-base-url]" ],
                actions: [ "report_progress" ]
            },
            CBEAD_INDEX: {
                name: "企业首页",
                urlPattern: /class\/index/,
                domPattern: [ "#app", ".activity-list" ],
                features: [ "vue", "spa" ],
                actions: [ "scan_courses" ]
            },
            CBEAD_COLUMN_TRAIN_NEW: {
                name: "企业专题详情页",
                urlPattern: /train-new\/class-detail/,
                hashPattern: /class-detail\/([a-f0-9-]+)/,
                domPattern: [ ".activity-stage", ".list" ],
                apiEndpoint: "/inc/nc/course/pd/getPackById",
                actions: [ "extract_from_api", "extract_from_dom" ]
            },
            CBEAD_PLAYER: {
                name: "企业播放页",
                urlPattern: /study\/course\/detail/,
                domPattern: [ ".player-content", ".new-global-height", "video" ],
                actions: [ "report_progress" ]
            },
            UNKNOWN: {
                name: "未知页面",
                actions: [ "detect_features" ]
            }
        },
        API_CONFIG: {
            PORTAL: {
                baseUrl: "https://www.cela.gov.cn",
                version: "v1",
                endpoints: {},
                supported: false
            },
            PUDONG: {
                baseUrl: "auto",
                version: "v1",
                endpoints: {
                    GET_PLAY_TREND: "/inc/nc/course/play/getPlayTrend",
                    GET_STUDY_RECORD: "/inc/nc/course/study/getStudyRecord",
                    GET_COURSE_LIST: "/inc/nc/course/getCourseList",
                    GET_PACK_BY_ID: "/inc/nc/course/pd/getPackById",
                    GET_CHANNEL_INFO: "/inc/nc/channel/getChannelInfo",
                    PULSE_SAVE_RECORD: "/api/player/pulse/saveRecord",
                    REPORT_PROGRESS: "/inc/nc/course/play/reportProgress",
                    GET_COURSE_LIST_FROM_CHANNEL: "/inc/nc/channel/getCourseList"
                },
                variants: {
                    main: {
                        host: "cela.e-celap.cn",
                        endpoints: {}
                    },
                    pudong: {
                        host: "pudong.e-celap.cn",
                        endpoints: {}
                    }
                },
                fallback: {
                    GET_COURSE_LIST: [ "/inc/nc/course/pd/getPackById", "/inc/nc/channel/getCourseList", "/api/course/list" ],
                    PULSE_SAVE_RECORD: [ "/api/player/pulse/saveRecord", "/inc/nc/course/play/pulseSaveRecord", "/api/player/pulse" ]
                },
                requestConfig: {
                    timeout: 1e4,
                    retries: 3,
                    retryDelay: 1e3
                }
            },
            GWYPX: {
                baseUrl: "https://cela.gwypx.com.cn",
                supported: false
            },
            CBEAD: {
                baseUrl: "https://cela.cbead.cn",
                version: "v1",
                endpoints: {
                    GET_PLAY_TREND: "/inc/nc/course/play/getPlayTrend",
                    GET_STUDY_RECORD: "/inc/nc/course/study/getStudyRecord",
                    GET_COURSE_LIST: "/inc/nc/course/getCourseList",
                    PULSE_SAVE_RECORD: "/inc/nc/course/play/pulseSaveRecord",
                    GET_COURSEWARE_DETAIL: "/inc/nc/course/play/getCoursewareDetail",
                    GET_PACK_BY_ID: "/inc/nc/course/pd/getPackById"
                },
                supported: true,
                fallback: {
                    PULSE_SAVE_RECORD: [ "/inc/nc/course/play/pulseSaveRecord", "/api/player/pulse/saveRecord" ]
                },
                requestConfig: {
                    timeout: 1e4,
                    retries: 3,
                    retryDelay: 1e3
                }
            }
        }
    };
    const EventBus = {
        events: {},
        subscribe(event, listener) {
            if (!this.events[event]) {
                this.events[event] = [];
            }
            this.events[event].push(listener);
            return () => {
                const index = this.events[event].indexOf(listener);
                if (index > -1) {
                    this.events[event].splice(index, 1);
                }
            };
        },
        publish(event, data) {
            if (!this.events[event]) return;
            this.events[event].forEach(listener => {
                try {
                    listener(data);
                } catch (error) {
                    console.error(`EventBus error in ${event}:`, error);
                }
            });
        },
        once(event, listener) {
            const unsubscribe = this.subscribe(event, data => {
                unsubscribe();
                listener(data);
            });
            return unsubscribe;
        }
    };
    const Utils = {
        formatTime(seconds) {
            if (!seconds || seconds < 0) return "00:00:00";
            const hours = Math.floor(seconds / 3600);
            const minutes = Math.floor(seconds % 3600 / 60);
            const secs = seconds % 60;
            return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
        },
        parseTimeToSeconds(timeStr) {
            try {
                if (!timeStr) return 0;
                const parts = timeStr.split(":").map(part => parseInt(part, 10));
                if (parts.length === 3 && !parts.some(isNaN)) {
                    return parts[0] * 3600 + parts[1] * 60 + parts[2];
                }
                return 0;
            } catch {
                return 0;
            }
        },
        parseDuration(durationStr) {
            if (!durationStr || typeof durationStr !== "string") {
                return CONSTANTS.TIME_FORMATS.DEFAULT_DURATION;
            }
            return this.parseTimeToSeconds(durationStr) || CONSTANTS.TIME_FORMATS.DEFAULT_DURATION;
        }
    };
    var utils = Object.freeze({
        __proto__: null,
        Utils: Utils
    });
    const ServiceLocator = {
        services: {},
        register: function(name, service) {
            if (!name || typeof name !== "string") {
                throw new Error("ServiceLocator.register: name must be a non-empty string");
            }
            if (!service || typeof service !== "object") {
                throw new Error(`ServiceLocator.register: service must be an object (got: ${typeof service})`);
            }
            this.services[name] = service;
        },
        get: function(name) {
            return this.services[name] || null;
        },
        has: function(name) {
            return name in this.services;
        }
    };
    const ServiceNames = Object.freeze({
        API: "api",
        LEARNER: "learner",
        UI: "ui",
        CONFIG: "config"
    });
    const LearningState = {
        _failed: false,
        _reason: null,
        markFailed(reason) {
            this._failed = true;
            this._reason = reason;
            console.error(`[LearningState] 🚨 课程已标记为失败: ${reason}`);
        },
        reset() {
            this._failed = false;
            this._reason = null;
            console.log("[LearningState] 🔄 学习状态已重置");
        },
        isFailed() {
            return this._failed;
        },
        getFailureReason() {
            return this._reason;
        },
        getState() {
            return {
                failed: this._failed,
                reason: this._reason
            };
        }
    };
    const Settings = {
        defaultConfig: {
            LEARNING_STRATEGY: "default",
            SKIP_COMPLETED_COURSES: true,
            STUDY_RECORD_ENABLED: true,
            FALLBACK_MODE: false,
            DEBUG_MODE: false,
            HEARTBEAT_INTERVAL: 10,
            COMPLETION_THRESHOLD: 95,
            MAX_RETRY_ATTEMPTS: 10,
            RETRY_DELAY: 3e3,
            COURSE_COMPLETION_DELAY: 5,
            PUDONG_MODE: false,
            PUDONG_API_BASE: "",
            CBEAD_MODE: false,
            CBEAD_API_BASE: "",
            IS_PORTAL: false,
            SUPER_FAST_MODE: true,
            FAST_LEARNING_MODE: true
        },
        config: {},
        load() {
            this.config = {
                ...this.defaultConfig
            };
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "✅ 使用固定配置:默认学习模式",
                type: "success"
            });
        },
        get(key) {
            return this.config[key];
        },
        set(key, value) {
            this.config[key] = value;
        }
    };
    const CONFIG$1 = new Proxy(Settings.config, {
        get(target, prop) {
            return Settings.get(prop) ?? target[prop];
        },
        set(target, prop, value) {
            Settings.set(prop, value);
            target[prop] = value;
            return true;
        }
    });
    var infraConfig = Object.freeze({
        __proto__: null,
        CONFIG: CONFIG$1,
        Settings: Settings
    });
    const RequestQueue = {
        queue: [],
        activeCount: 0,
        maxConcurrent: 2,
        requestGap: 1e3,
        add(fn) {
            return new Promise((resolve, reject) => {
                this.queue.push({
                    fn: fn,
                    resolve: resolve,
                    reject: reject
                });
                this.process();
            });
        },
        async process() {
            if (this.activeCount >= this.maxConcurrent || this.queue.length === 0) return;
            this.activeCount++;
            const {fn: fn, resolve: resolve, reject: reject} = this.queue.shift();
            try {
                const delay = this.requestGap + Math.random() * 500;
                if (delay > 0) await new Promise(r => setTimeout(r, delay));
                const result = await fn();
                resolve(result);
            } catch (e) {
                reject(e);
            } finally {
                this.activeCount--;
                setTimeout(() => this.process(), 100);
            }
        }
    };
    const SIGN_UP_PATTERNS = [ "我要报名", "立即报名", "立即加入", "报名学习", "加入学习" ];
    function findSignUpButton() {
        const xpathConditions = SIGN_UP_PATTERNS.map(pattern => `contains(., "${pattern}")`).join(" or ");
        const xpath = `//button[${xpathConditions}] | //a[${xpathConditions}]`;
        const result = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        if (result.snapshotLength > 0) {
            console.log(`[DOMHelper] ✅ 通过 XPath 找到报名按钮,共 ${result.snapshotLength} 个`);
            return result.snapshotItem(0);
        }
        const selectors = [ "button", "a", ".btn", '[class*="button"]', '[class*="btn"]', '[role="button"]', ".el-button", ".ant-btn" ];
        for (const selector of selectors) {
            const elements = document.querySelectorAll(selector);
            for (const btn of elements) {
                const text = btn.textContent?.trim() || "";
                if (SIGN_UP_PATTERNS.some(pattern => text.includes(pattern))) {
                    console.log(`[DOMHelper] ✅ 通过选择器 "${selector}" 找到报名按钮`);
                    console.log(`[DOMHelper] 按钮文本: "${text}"`);
                    console.log(`[DOMHelper] 按钮标签: ${btn.tagName}, class: ${btn.className}`);
                    return btn;
                }
            }
        }
        console.log(`[DOMHelper] ⚠️ 未找到报名按钮,页面可能已报名`);
        return null;
    }
    function getSignUpButtonText(button) {
        return button?.textContent?.trim() || "报名按钮";
    }
    const MASK_SELECTORS = [ "#D339registerMask", '[id*="registerMask"]', '[class*="register-mask"]' ];
    const MASK_BUTTON_SELECTORS = [ ".register-img", '[class*="register-img"]', ".vjs-big-play-button", ".vjs-play-control" ];
    function clickMaskButton() {
        let clickedCount = 0;
        const clickDetails = [];
        for (const selector of MASK_BUTTON_SELECTORS) {
            const elements = document.querySelectorAll(selector);
            for (const el of elements) {
                if (el.offsetParent !== null) {
                    const btnInfo = {
                        selector: selector,
                        tagName: el.tagName,
                        id: el.id,
                        className: el.className
                    };
                    clickDetails.push(btnInfo);
                    el.click();
                    clickedCount++;
                    console.log(`[DOMHelper] 🖱️ 点击遮罩按钮: ${el.tagName}.${el.className}`);
                }
            }
        }
        if (clickedCount > 0) {
            console.log(`[DOMHelper] 🖱️ 已点击 ${clickedCount} 个遮罩按钮`);
        }
        return {
            clicked: clickedCount,
            buttons: clickDetails
        };
    }
    function detectMask() {
        const masks = [];
        for (const selector of MASK_SELECTORS) {
            const elements = document.querySelectorAll(selector);
            for (const el of elements) {
                if (el.offsetParent !== null || el.style.display !== "none") {
                    masks.push({
                        selector: selector,
                        tagName: el.tagName,
                        id: el.id,
                        className: el.className
                    });
                }
            }
        }
        return {
            exists: masks.length > 0,
            masks: masks
        };
    }
    function startMaskObserver() {
        const observer = new MutationObserver(mutations => {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        for (const selector of MASK_SELECTORS) {
                            const baseSelector = selector.split(":")[0];
                            if (node.matches && node.matches(baseSelector)) {
                                console.log(`[DOMHelper] 🛡️ 检测到遮罩: ${node.tagName}#${node.id || ""}.${node.className || ""}`);
                                setTimeout(() => clickMaskButton(), 100);
                                break;
                            }
                        }
                    }
                }
            }
        });
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
        console.log("[DOMHelper] 🛡️ 遮罩监听器已启动");
        return observer;
    }
    var infraDomHelper = Object.freeze({
        __proto__: null,
        clickMaskButton: clickMaskButton,
        detectMask: detectMask,
        findSignUpButton: findSignUpButton,
        getSignUpButtonText: getSignUpButtonText,
        startMaskObserver: startMaskObserver
    });
    function getUI() {
        return typeof window !== "undefined" && window.UI ? window.UI : null;
    }
    function detectEnvironment() {
        const hostname = window.location.hostname;
        const pathname = window.location.pathname;
        const hash = window.location.hash;
        const result = {
            currentEnv: null,
            hostname: hostname,
            pathname: pathname,
            hash: hash,
            confidence: 0,
            features: {}
        };
        for (const [envKey, envConfig] of Object.entries(CONSTANTS.ENVIRONMENTS)) {
            if (envConfig.hostnames.some(host => hostname === host || hostname.endsWith("." + host))) {
                result.currentEnv = envKey;
                result.confidence = 50;
                console.log(`🔍 域名匹配: ${envKey} (${envConfig.name})`);
                break;
            }
        }
        if (result.currentEnv) {
            const envConfig = CONSTANTS.ENVIRONMENTS[result.currentEnv];
            if (envConfig.pathPatterns) {
                const fullPath = pathname + hash;
                const matchesPath = envConfig.pathPatterns.some(pattern => fullPath.includes(pattern));
                if (matchesPath) {
                    result.confidence += 30;
                    console.log(`✅ 路径验证通过 (+30分)`);
                }
            }
        }
        if (result.currentEnv) {
            const envConfig = CONSTANTS.ENVIRONMENTS[result.currentEnv];
            if (envConfig.features) {
                if (envConfig.features.framework === "vue") {
                    if (document.querySelector("#app") || window.Vue) {
                        result.confidence += 15;
                        result.features.framework = "vue";
                        console.log(`✅ Vue框架特征检测 (+15分)`);
                    }
                }
                if (envConfig.features.framework === "jquery") {
                    if (window.jQuery && !document.querySelector("#app")) {
                        result.confidence += 15;
                        result.features.framework = "jquery";
                        console.log(`✅ jQuery框架特征检测 (+15分)`);
                    }
                }
                if (envConfig.features.courseContainer) {
                    const container = document.querySelector(envConfig.features.courseContainer);
                    if (container) {
                        result.confidence += 5;
                        console.log(`✅ 容器特征检测 (${envConfig.features.courseContainer}, +5分)`);
                    }
                }
            }
        }
        const envConfig = result.currentEnv ? CONSTANTS.ENVIRONMENTS[result.currentEnv] : null;
        if (result.currentEnv === "PORTAL") {
            CONFIG$1.IS_PORTAL = true;
            console.log("🏠 检测到中国干部网络学院门户页面");
        } else if (result.currentEnv === "PUDONG") {
            CONFIG$1.PUDONG_MODE = true;
            CONFIG$1.PUDONG_API_BASE = `https://${hostname}`;
            console.log("🏢 检测到浦东分院环境");
        } else if (result.currentEnv === "GWYPX") {
            CONFIG$1.UNSUPPORTED_BRANCH = "党校分院";
        } else if (result.currentEnv === "CBEAD") {
            CONFIG$1.CBEAD_MODE = true;
            CONFIG$1.CBEAD_API_BASE = `https://${hostname}`;
            console.log("🏢 检测到企业分院环境");
        }
        console.log(`🌐 环境检测完成: ${result.currentEnv || "UNKNOWN"} (置信度: ${result.confidence}%)`);
        console.log(`   - 域名: ${hostname}`);
        console.log(`   - 路径: ${pathname}`);
        console.log(`   - 特征: ${JSON.stringify(result.features)}`);
        EventBus.publish("environment:detected", {
            ...result,
            pudongMode: CONFIG$1.PUDONG_MODE,
            isPortal: CONFIG$1.IS_PORTAL,
            unsupportedBranch: CONFIG$1.UNSUPPORTED_BRANCH
        });
        setTimeout(() => {
            const UI = getUI();
            if (!UI || !UI.setIncompatible) return;
            if (CONFIG$1.UNSUPPORTED_BRANCH && envConfig) {
                UI.setIncompatible(`⚠️ 当前检测到【${envConfig.name}】,${envConfig.reason}`);
            } else if (CONFIG$1.IS_PORTAL && envConfig) {
                UI.setIncompatible(`🏠 ${envConfig.reason}`);
            } else if (!CONFIG$1.PUDONG_MODE && !CONFIG$1.IS_PORTAL && !CONFIG$1.CBEAD_MODE) {
                UI.setIncompatible("🔍 当前域名未被识别为受支持的学习环境,脚本已停止加载。");
            }
        }, 100);
        return result;
    }
    const PUDONG_CONSTANTS = {
        PATH_PATTERNS: {
            INDEX: "/pc/nc/pagehome/index",
            COLUMN: [ "zgpdyxkc", "specialcolumn", "specialdetail", "channelDetail", "pdchanel/pdzq" ],
            PLAYER: "coursePlayer"
        },
        PAGE_TYPES: {
            INDEX: "index",
            COLUMN: "column",
            PLAYER: "player",
            UNKNOWN: "unknown"
        },
        SELECTORS: {
            COURSE_ITEMS: [ ".dsf_nc_zg_item", ".dsf_nc_pd_course_express_item", ".dsf-many-schedule-course-list-row", ".dsf_nc_pd_special_item", ".pd_course_item", ".dsjy_card" ],
            ENTER_BTN: ".course-enter-btn",
            PLAYER_CONTAINER: "#coursePlayer"
        }
    };
    const Compliance = {
        enforce(url, data) {
            if (!data) return data;
            if (url.includes("/pulseSaveRecord") || url.includes("pulseSaveRecord")) {
                return this._enforcePulseRecord(data);
            }
            return data;
        },
        _enforcePulseRecord(data) {
            let params;
            let isString = typeof data === "string";
            if (isString) {
                params = new URLSearchParams(data);
            } else if (data instanceof FormData) {
                return data;
            } else {
                params = new URLSearchParams(data);
            }
            params.set("pulseTime", "10");
            params.set("pulseRate", "1");
            if (params.has("progress")) {
                const progress = parseInt(params.get("progress"));
                if (progress > 98) {
                    console.warn("[Compliance] ⚠️ 拦截异常进度上报:", progress, "-> 强制修正为 98");
                    params.set("progress", "98");
                }
            }
            return isString ? params.toString() : Object.fromEntries(params);
        }
    };
    const ApiCore = {
        _cachedToken: null,
        abortController: null,
        getBaseUrl() {
            const config = ServiceLocator.get(ServiceNames.CONFIG);
            if (config?.CBEAD_MODE) {
                return config?.CBEAD_API_BASE || `https://${window.location.hostname}`;
            }
            if (config?.PUDONG_MODE) {
                return config?.PUDONG_API_BASE || `https://${window.location.hostname}`;
            }
            return config?.PUDONG_API_BASE || config?.CBEAD_API_BASE || `https://${window.location.hostname}`;
        },
        _prepareHeaders(customHeaders = {}, data = null) {
            const token = this._extractToken();
            const headers = {
                Accept: "application/json, text/plain, */*",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
                "X-Requested-With": "XMLHttpRequest",
                Referer: window.location.href,
                Origin: this.getBaseUrl(),
                Cookie: document.cookie,
                ...customHeaders
            };
            if (!(data instanceof FormData)) {
                if (typeof data === "string" && data.includes("=")) {
                    headers["Content-Type"] = "application/x-www-form-urlencoded";
                } else if (data) {
                    headers["Content-Type"] = "application/json";
                }
            }
            if (token) {
                headers["Authorization"] = `Bearer ${token}`;
                headers["X-Auth-Token"] = token;
            }
            return headers;
        },
        _handleResponse(response, resolve, reject) {
            if (response.status === 401) {
                this._cachedToken = null;
                this._log("Token 可能已过期 (401),清除缓存", "warn");
            }
            const config = ServiceLocator.get(ServiceNames.CONFIG);
            if (config?.DEBUG_MODE) {
                this._log(`${response.status} ${response.responseText?.substring(0, 100)}...`);
            }
            try {
                if (response.responseText && response.responseText.trim()) {
                    return resolve(JSON.parse(response.responseText));
                }
                if (response.status >= 200 && response.status < 300) {
                    return resolve({
                        code: response.status,
                        success: true,
                        message: "Success"
                    });
                }
                resolve({
                    status: response.status,
                    message: "Empty response"
                });
            } catch {
                const html = response.responseText || "";
                if (html.trim().startsWith("<")) {
                    if (html.includes("login") || html.includes("登录")) {
                        this._log("登录已失效,请重新登录", "error");
                        alert("cela学习助手:登录已失效,请刷新页面重新登录!");
                        const learner = ServiceLocator.get(ServiceNames.LEARNER);
                        if (learner) learner.stop();
                        EventBus.publish(CONSTANTS.EVENTS.LEARNING_STOP);
                    } else if (html.includes("verification") || html.includes("验证码") || html.includes("人机")) {
                        this._log("触发人机验证,请手动完成验证", "error");
                        alert('cela学习助手:触发人机验证!请在页面上完成验证后点击"开始学习"继续。');
                        const learner = ServiceLocator.get(ServiceNames.LEARNER);
                        if (learner) learner.stop();
                        EventBus.publish(CONSTANTS.EVENTS.LEARNING_STOP);
                    }
                    return resolve({
                        error: "HTML response received",
                        status: response.status,
                        isHtml: true
                    });
                }
                resolve({
                    status: response.status,
                    message: html || "Empty response",
                    success: response.status >= 200 && response.status < 300
                });
            }
        },
        async request(options) {
            return RequestQueue.add(() => new Promise((resolve, reject) => {
                if (this.abortController && this.abortController.signal.aborted) {
                    return reject(new DOMException("Aborted", "AbortError"));
                }
                if (options.data) {
                    options.data = Compliance.enforce(options.url, options.data);
                }
                const headers = this._prepareHeaders(options.headers, options.data);
                const config = ServiceLocator.get(ServiceNames.CONFIG);
                if (config?.DEBUG_MODE) {
                    this._log(`${options.method || "GET"} ${options.url}`);
                }
                const req = GM_xmlhttpRequest({
                    method: options.method || "GET",
                    url: options.url,
                    headers: headers,
                    data: options.data,
                    timeout: options.timeout || 3e4,
                    onload: res => this._handleResponse(res, resolve, reject),
                    onerror: err => {
                        this._log(`请求失败: ${err.message}`, "error");
                        reject(err);
                    },
                    ontimeout: () => {
                        this._log("请求超时", "error");
                        reject(new Error("请求超时"));
                    }
                });
                if (this.abortController) {
                    this.abortController.signal.addEventListener("abort", () => {
                        if (req.abort) req.abort();
                        reject(new DOMException("Aborted", "AbortError"));
                    });
                }
            }));
        },
        async get(url, options = {}) {
            return this.request({
                ...options,
                method: "GET",
                url: url
            });
        },
        async post(url, data, options = {}) {
            return this.request({
                ...options,
                method: "POST",
                url: url,
                data: data
            });
        },
        _extractToken() {
            if (this._cachedToken) return this._cachedToken;
            const sources = [ () => localStorage.getItem(CONSTANTS.STORAGE_KEYS.TOKEN), () => localStorage.getItem(CONSTANTS.STORAGE_KEYS.AUTH_TOKEN), () => localStorage.getItem(CONSTANTS.STORAGE_KEYS.ACCESS_TOKEN), () => sessionStorage.getItem(CONSTANTS.STORAGE_KEYS.TOKEN), () => sessionStorage.getItem(CONSTANTS.STORAGE_KEYS.AUTH_TOKEN), () => document.querySelector('meta[name="csrf-token"]')?.getAttribute("content"), () => window.token, () => window.authToken, () => {
                const match = document.cookie.match(CONSTANTS.COOKIE_PATTERNS.TOKEN);
                return match ? match[1] : null;
            } ];
            for (const source of sources) {
                try {
                    const token = source();
                    if (token && token.length > 10) {
                        this._cachedToken = token;
                        this._log(`找到认证token: ${token.substring(0, 20)}...`, "debug");
                        return token;
                    }
                } catch {}
            }
            this._log("未找到认证token", "debug");
            return null;
        },
        _log(message, level = "info") {
            const ui = ServiceLocator.get(ServiceNames.UI);
            if (ui) {
                ui.log(message, level);
            } else {
                console.log(`[ApiCore] ${message}`);
            }
        },
        setAbortController(controller) {
            this.abortController = controller;
        },
        clearTokenCache() {
            this._cachedToken = null;
        }
    };
    const CourseAdapter = {
        normalize(raw, source = "api") {
            return {
                id: raw.id || raw.businessId || raw.courseId,
                courseId: raw.courseId || raw.id || raw.businessId,
                dsUnitId: raw.dsUnitId || raw.unitId || (raw.unitOrder && raw.order ? `unit_${raw.unitOrder}_${raw.order}` : "unit_default"),
                title: raw.name || raw.title || raw.courseName || "未命名课程",
                courseName: raw.name || raw.title || raw.courseName || "未命名课程",
                teacher: raw.teacher || "",
                durationStr: raw.duration || raw.durationStr || raw.timeLength || "00:30:00",
                period: raw.period || 0,
                status: raw.status || "not_started",
                source: source
            };
        }
    };
    function debugLog$2(...args) {}
    const PUDONG_API_CONFIG = {
        baseUrl: null,
        endpoints: {
            GET_PLAY_TREND: "/inc/nc/course/play/getPlayTrend",
            GET_COURSE_LIST: "/inc/nc/course/list",
            PULSE_SAVE_RECORD: "/inc/nc/course/play/pulseSaveRecord",
            PULSE_SAVE_RECORD_ALT: "/api/player/pulse",
            GET_STUDY_RECORD: "/inc/nc/course/play/getStudyRecord",
            GET_COURSEWARE_LIST: "/inc/nc/course/play/getPlayTrend",
            GET_COURSEWARE_LIST_ALT: "/inc/nc/course/play/getPlayInfoById"
        },
        getBaseUrl() {
            const config = ServiceLocator.get(ServiceNames.CONFIG);
            this.baseUrl = config?.PUDONG_API_BASE || `https://${window.location.hostname}`;
            return this.baseUrl;
        },
        getUrl(endpoint) {
            const baseUrl = this.getBaseUrl();
            const endpointPath = this.endpoints[endpoint] || endpoint;
            return baseUrl + endpointPath;
        }
    };
    function _buildUrl$1(endpoint, params = {}) {
        let url = PUDONG_API_CONFIG.getUrl(endpoint);
        const queryString = new URLSearchParams({
            ...params,
            _t: Date.now()
        }).toString();
        return `${url}?${queryString}`;
    }
    const PudongApi = {
        ...ApiCore,
        isSuccessResponse(result) {
            return result && (result.success === true || result.code === 200 || result.code === 2e4 || result.state === 2e4 || result.status === "success" || result.status === "ok" || result.code >= 200 && result.code < 300 || result.result === "success" || result.success === 1);
        },
        async getPlayInfo(courseId, coursewareId = null, courseDuration = null) {
            const {Utils: Utils} = await Promise.resolve().then(function() {
                return utils;
            });
            try {
                debugLog$2(`[getPlayInfo] 获取课程 ${courseId} 的播放信息`);
                const response = await this.get(_buildUrl$1("GET_PLAY_TREND", {
                    courseId: courseId
                }));
                if (response?.success && response?.data) {
                    const data = response.data;
                    let videoId = null;
                    let duration = 0;
                    let lastLearnedTime = 0;
                    let foundCoursewareId = coursewareId;
                    if (coursewareId && data.playTree?.children) {
                        const target = data.playTree.children.find(c => String(c.id) === String(coursewareId));
                        if (target) {
                            videoId = target.id;
                            foundCoursewareId = target.id;
                            duration = target.sumDurationLong || 0;
                            lastLearnedTime = target.lastWatchPoint ? Utils.parseTimeToSeconds(target.lastWatchPoint) : 0;
                            debugLog$2(`成功匹配到课件: ${target.title}`);
                        }
                    }
                    if (!videoId && data.locationSite) {
                        videoId = data.locationSite.id;
                        foundCoursewareId = data.locationSite.id;
                        duration = data.locationSite.sumDurationLong || 0;
                        lastLearnedTime = data.locationSite.lastWatchPoint ? Utils.parseTimeToSeconds(data.locationSite.lastWatchPoint) : 0;
                    }
                    if (duration === 0 && courseDuration) {
                        duration = Utils.parseDuration(courseDuration);
                    }
                    if (duration === 0) {
                        duration = CONSTANTS.TIME_FORMATS.DEFAULT_DURATION;
                    }
                    if (!videoId) {
                        videoId = `mock_video_${courseId}`;
                        debugLog$2("无法获取真实 videoId,使用模拟ID");
                    }
                    return {
                        courseId: courseId,
                        coursewareId: foundCoursewareId,
                        videoId: videoId,
                        duration: duration,
                        lastLearnedTime: lastLearnedTime,
                        playURL: `https://zpyapi.shsets.com/player/get?videoId=${videoId}`,
                        dataSource: videoId.startsWith("mock_") ? "fallback" : "api"
                    };
                }
                return null;
            } catch (error) {
                debugLog$2(`[getPlayInfo] 出错: ${error.message}`);
                return null;
            }
        },
        async reportProgress(playInfo, currentTime) {
            const {Utils: Utils} = await Promise.resolve().then(function() {
                return utils;
            });
            const watchPoint = Utils.formatTime(currentTime);
            const progress = Math.round(currentTime / playInfo.duration * 100);
            const payload = new URLSearchParams({
                courseId: playInfo.courseId,
                coursewareId: playInfo.coursewareId || playInfo.videoId,
                videoId: playInfo.videoId || "",
                watchPoint: watchPoint,
                currentTime: currentTime,
                duration: playInfo.duration,
                progress: progress,
                pulseTime: 10,
                pulseRate: 1,
                _t: Date.now()
            }).toString();
            try {
                return await this.post(_buildUrl$1("PULSE_SAVE_RECORD"), payload, {
                    headers: {
                        "Content-Type": "application/x-www-form-urlencoded"
                    }
                });
            } catch (error) {
                return await this.post(_buildUrl$1("PULSE_SAVE_RECORD_ALT"), payload, {
                    headers: {
                        "Content-Type": "application/x-www-form-urlencoded"
                    }
                });
            }
        },
        async reportProgressWithDelay(playInfo, currentTime) {
            const progressPercent = Math.round(currentTime / playInfo.duration * 100);
            if (progressPercent > 90) {
                await new Promise(resolve => setTimeout(resolve, 1e3 + Math.random() * 1e3));
            }
            return await this.reportProgress(playInfo, currentTime);
        },
        async checkCompletion(courseId, coursewareId = null) {
            try {
                const response = await this.get(_buildUrl$1("GET_PLAY_TREND", {
                    courseId: courseId
                }));
                const config = ServiceLocator.get(ServiceNames.CONFIG);
                if (response?.success && response?.data) {
                    const data = response.data;
                    if (coursewareId && data.playTree?.children) {
                        const target = data.playTree.children.find(c => String(c.id) === String(coursewareId));
                        if (target) {
                            const finishedRate = parseInt(target.finishedRate || 0);
                            return {
                                isCompleted: finishedRate >= (config?.COMPLETION_THRESHOLD || 80),
                                finishedRate: finishedRate,
                                method: "playTree_match"
                            };
                        }
                    }
                    if (data.locationSite && data.locationSite.finishedRate !== undefined) {
                        const finishedRate = parseInt(data.locationSite.finishedRate);
                        return {
                            isCompleted: finishedRate >= (config?.COMPLETION_THRESHOLD || 80),
                            finishedRate: finishedRate,
                            method: "playTrend_total"
                        };
                    }
                }
                return {
                    isCompleted: false,
                    finishedRate: 0,
                    method: "default"
                };
            } catch (error) {
                debugLog$2(`完成度检查失败: ${error.message}`);
                return {
                    isCompleted: false,
                    finishedRate: 0,
                    method: "error"
                };
            }
        },
        async getCoursewareList(courseId) {
            try {
                debugLog$2(`正在获取课程包详细信息 (ID: ${courseId})...`);
                const endpoints = [ _buildUrl$1("GET_COURSEWARE_LIST", {
                    courseId: courseId
                }), _buildUrl$1("GET_COURSEWARE_LIST_ALT", {
                    id: courseId
                }) ];
                for (const endpoint of endpoints) {
                    try {
                        const response = await this.get(endpoint);
                        if (response && response.success && response.data) {
                            const data = response.data;
                            if (data.playTree && data.playTree.children && Array.isArray(data.playTree.children)) {
                                const videos = data.playTree.children.filter(c => c.rTypeValue === "video" || c.rTypeValue === "courseware");
                                if (videos.length > 0) {
                                    debugLog$2(`从 playTree 获取到 ${videos.length} 个课件`);
                                    return videos.map((v, index) => CourseAdapter.normalize({
                                        id: courseId,
                                        courseId: courseId,
                                        dsUnitId: v.id,
                                        title: v.title || `${data.title || "课程"} - 视频${index + 1}`,
                                        duration: v.sumDurationLong || 0
                                    }, "pudong_api_tree"));
                                }
                            }
                            if (data.coursewareIdList && Array.isArray(data.coursewareIdList) && data.coursewareIdList.length > 0) {
                                debugLog$2(`从 coursewareIdList 获取到 ${data.coursewareIdList.length} 个课件`);
                                return data.coursewareIdList.map((cw, index) => CourseAdapter.normalize({
                                    id: courseId,
                                    courseId: courseId,
                                    dsUnitId: cw.id || cw.coursewareId,
                                    title: cw.name || cw.title || `${data.title || "课程"} - 视频${index + 1}`,
                                    duration: cw.duration || 0
                                }, "pudong_api_list"));
                            }
                            const list = data.subList || data.courseList || data.lessons;
                            if (list && Array.isArray(list) && list.length > 0) {
                                debugLog$2(`从 API 子列表获取到 ${list.length} 个视频`);
                                return list.map(item => CourseAdapter.normalize(item, "pudong_api_sublist"));
                            }
                        }
                    } catch {
                        continue;
                    }
                }
                return [];
            } catch (error) {
                debugLog$2(`获取课件列表失败: ${error.message}`);
                return [];
            }
        },
        async getCourseList() {
            try {
                debugLog$2("正在获取课程列表...");
                const currentUrl = window.location.href;
                if (currentUrl.toLowerCase().includes("specialdetail") || currentUrl.toLowerCase().includes("channeldetail") || currentUrl.toLowerCase().includes("pdchanel")) {
                    debugLog$2("检测到专题详情页,尝试从API获取课程列表...");
                    let channelId = null;
                    try {
                        const urlObj = new URL(currentUrl.replace("#", ""));
                        channelId = urlObj.searchParams.get("id");
                        if (!channelId) {
                            const hash = window.location.hash;
                            const match = hash.match(/[?&]id=([^&]+)/);
                            if (match) channelId = match[1];
                        }
                    } catch (error) {
                        debugLog$2(`解析频道ID失败: ${error.message}`);
                    }
                    if (channelId) {
                        debugLog$2(`专题ID: ${channelId}`);
                        return await this.getCourseListFromChannel(channelId);
                    }
                }
                debugLog$2("正在扫描页面课程元素...");
                for (let i = 0; i < 10; i++) {
                    const hasContent = document.querySelectorAll('.dsf_nc_pd_special_item, .list_item, .pd_course_item, .dsjy_card, .el-card, [class*="course"]').length > 0;
                    if (hasContent) break;
                    await new Promise(r => setTimeout(r, 500));
                }
                const courseList = [];
                let courseElements = [];
                const pudongItems = document.querySelectorAll(".dsf_nc_pd_special_item, .list_item, .pd_course_item, .dsjy_card");
                if (pudongItems.length > 0) {
                    debugLog$2(`找到浦东分院专用列表项: ${pudongItems.length}个`);
                    courseElements = Array.from(pudongItems);
                } else {
                    const selectors = [ ".dsf_nc_zg_item", ".dsf-many-schedule-course-list-row", ".dsf_nc_pd_course_express_item", ".el-card" ];
                    for (const selector of selectors) {
                        const elements = document.querySelectorAll(selector);
                        const validElements = Array.from(elements).filter(el => !el.closest("#api-learner-panel"));
                        if (validElements.length > 0) {
                            courseElements = validElements;
                            debugLog$2(`使用选择器 "${selector}" 找到 ${validElements.length} 个课程元素`);
                            break;
                        }
                    }
                }
                courseElements.forEach((el, index) => {
                    const findId = element => {
                        let current = element;
                        let depth = 0;
                        while (current && depth < 5) {
                            const id = current.getAttribute("data-id") || current.getAttribute("data-course-id") || current.getAttribute("id") || current.getAttribute("data-courseid") || current.querySelector("[data-id]")?.getAttribute("data-id") || current.querySelector("[data-course-id]")?.getAttribute("data-course-id");
                            if (id && !id.includes("kapture") && !id.includes("course_") && id.length > 5) return id;
                            current = current.parentElement;
                            depth++;
                        }
                        const uuidMatch = (element.getAttribute("onclick") || element.parentElement?.innerHTML || "").match(/[a-f0-9]{32}/);
                        return uuidMatch ? uuidMatch[0] : null;
                    };
                    const courseId = findId(el);
                    if (!courseId) return;
                    const rawData = {
                        courseId: courseId,
                        dsUnitId: el.getAttribute("data-unit-id") || el.getAttribute("data-dsunit") || `unit_${index}`,
                        courseName: el.querySelector(".title, .name, .course-title, .item_content, h3, h4")?.textContent?.trim() || el.getAttribute("title") || el.textContent?.trim()?.split("\n")[0]?.substring(0, 80) || `课程${index + 1}`,
                        durationStr: el.querySelector(".duration, .time, .period")?.textContent?.trim() || "00:30:00",
                        status: el.getAttribute("data-status") || "not_started"
                    };
                    if (rawData.courseName && rawData.courseName.length > 2) {
                        courseList.push(CourseAdapter.normalize(rawData, "pudong_dom"));
                    }
                });
                debugLog$2(`解析得到 ${courseList.length} 门课程`);
                return courseList;
            } catch (error) {
                debugLog$2(`获取课程列表失败: ${error.message}`);
                return [];
            }
        },
        async getCourseListFromChannel(channelId) {
            try {
                debugLog$2(`正在从频道获取课程列表 (ID: ${channelId})...`);
                const apiEndpoints = [ `/inc/nc/pack/getById?id=${channelId}&_t=${Date.now()}`, `/inc/nc/course/pd/getPackById?id=${channelId}&_t=${Date.now()}`, `/api/nc/channel/detail?id=${channelId}&_t=${Date.now()}`, `/inc/nc/course/getCourseList?channelId=${channelId}&_t=${Date.now()}` ];
                for (const endpoint of apiEndpoints) {
                    try {
                        debugLog$2(`尝试API端点: ${endpoint}`);
                        const response = await this.get(PUDONG_API_CONFIG.getBaseUrl() + endpoint);
                        if (response && response.success && response.data) {
                            const data = response.data;
                            const courseList = [];
                            if (data.pdChannelUnitList) {
                                for (const unit of data.pdChannelUnitList) {
                                    if (unit.subList) {
                                        for (const course of unit.subList) {
                                            if (course.typeValue === "course") {
                                                courseList.push(CourseAdapter.normalize({
                                                    ...course,
                                                    unitOrder: unit.order
                                                }, "pudong_api_unit"));
                                            }
                                        }
                                    }
                                }
                            } else if (data.subList && Array.isArray(data.subList)) {
                                debugLog$2(`从 subList 获取到 ${data.subList.length} 个课程`);
                                data.subList.forEach((item, index) => {
                                    courseList.push(CourseAdapter.normalize({
                                        courseId: item.id || item.courseId || item.dsId,
                                        dsUnitId: item.dsUnitId || item.id,
                                        courseName: item.courseName || item.title || item.name || `课程${index + 1}`,
                                        durationStr: item.durationStr || item.duration || "00:30:00",
                                        status: item.status || "not_started"
                                    }, "pudong_api_sublist"));
                                });
                            } else if (data.courseList && Array.isArray(data.courseList)) {
                                debugLog$2(`从 courseList 获取到 ${data.courseList.length} 个课程`);
                                data.courseList.forEach((item, index) => {
                                    courseList.push(CourseAdapter.normalize({
                                        courseId: item.id || item.courseId || item.dsId,
                                        dsUnitId: item.dsUnitId || item.id,
                                        courseName: item.courseName || item.title || item.name || `课程${index + 1}`,
                                        durationStr: item.durationStr || item.duration || "00:30:00",
                                        status: item.status || "not_started"
                                    }, "pudong_api_courselist"));
                                });
                            }
                            if (courseList.length > 0) {
                                debugLog$2(`从API获取到 ${courseList.length} 门课程`);
                                return courseList;
                            }
                        }
                    } catch (error) {
                        debugLog$2(`API端点 ${endpoint} 失败: ${error.message}`);
                        continue;
                    }
                }
                debugLog$2("所有频道API端点都失败了");
                return [];
            } catch (error) {
                debugLog$2(`从频道获取课程列表失败: ${error.message}`);
                return [];
            }
        }
    };
    function debugLog$1(...args) {}
    const PudongScanner = {
        async scanCourses(selectors = PUDONG_CONSTANTS.SELECTORS) {
            try {
                const currentUrl = window.location.href;
                if (currentUrl.toLowerCase().includes("channeldetail") || currentUrl.toLowerCase().includes("specialdetail") || currentUrl.toLowerCase().includes("pdchanel")) {
                    debugLog$1("检测到专题详情页,调用 API 获取课程列表...");
                    const apiCourses = await PudongApi.getCourseList();
                    if (apiCourses && apiCourses.length > 0) {
                        debugLog$1(`API 返回 ${apiCourses.length} 门课程`);
                        return apiCourses;
                    }
                    debugLog$1("API 未返回课程,降级到 DOM 扫描");
                }
                await this._waitForCourseElements();
                const courseElements = this._findCourseElements(selectors);
                if (courseElements.length === 0) {
                    debugLog$1("未找到课程元素");
                    return [];
                }
                debugLog$1(`找到 ${courseElements.length} 个课程元素`);
                const courses = this._parseCourseElements(courseElements);
                const uniqueCourses = this._uniqueCourses(courses);
                debugLog$1(`解析得到 ${uniqueCourses.length} 门有效课程`);
                return uniqueCourses;
            } catch (error) {
                console.error(`[PudongScanner] 扫描失败: ${error.message}`);
                return [];
            }
        },
        async _waitForCourseElements() {
            for (let i = 0; i < 10; i++) {
                const found = PUDONG_CONSTANTS.SELECTORS.COURSE_ITEMS.some(s => document.querySelector(s));
                if (found) break;
                await new Promise(r => setTimeout(r, 500));
            }
        },
        _findCourseElements(selectors) {
            const pudongItems = document.querySelectorAll(".dsf_nc_pd_special_item, .list_item, .pd_course_item, .dsjy_card");
            if (pudongItems.length > 0) {
                return Array.from(pudongItems);
            }
            for (const selector of selectors.COURSE_ITEMS) {
                const elements = document.querySelectorAll(selector);
                const validElements = Array.from(elements).filter(el => !el.closest("#api-learner-panel"));
                if (validElements.length > 0) {
                    return validElements;
                }
            }
            return [];
        },
        _parseCourseElements(elements) {
            const courseList = [];
            elements.forEach((el, index) => {
                const courseId = this._extractCourseId(el);
                if (!courseId) return;
                const rawData = {
                    courseId: courseId,
                    dsUnitId: el.getAttribute("data-unit-id") || el.getAttribute("data-dsunit") || `unit_${index}`,
                    courseName: this._extractCourseName(el),
                    durationStr: this._extractDuration(el),
                    status: el.getAttribute("data-status") || "not_started"
                };
                if (rawData.courseName && rawData.courseName.length > 2) {
                    courseList.push(CourseAdapter.normalize(rawData, "pudong_dom"));
                }
            });
            return courseList;
        },
        _extractCourseId(element) {
            let current = element;
            let depth = 0;
            while (current && depth < 5) {
                const id = current.getAttribute("data-id") || current.getAttribute("data-course-id") || current.getAttribute("id") || current.getAttribute("data-courseid") || current.querySelector("[data-id]")?.getAttribute("data-id") || current.querySelector("[data-course-id]")?.getAttribute("data-course-id");
                if (id && !id.includes("kapture") && !id.includes("course_") && id.length > 5) {
                    return id;
                }
                current = current.parentElement;
                depth++;
            }
            const uuidMatch = (element.getAttribute("onclick") || element.parentElement?.innerHTML || "").match(/[a-f0-9]{32}/);
            return uuidMatch ? uuidMatch[0] : null;
        },
        _extractCourseName(element) {
            return element.querySelector(".title, .name, .course-title, .item_content, h3, h4")?.textContent?.trim() || element.getAttribute("title") || element.textContent?.trim()?.split("\n")[0]?.substring(0, 80) || `课程${Date.now()}`;
        },
        _extractDuration(element) {
            return element.querySelector(".duration, .time, .period")?.textContent?.trim() || "00:30:00";
        },
        _uniqueCourses(courses) {
            return courses.filter((course, index, self) => index === self.findIndex(c => c.courseId === course.courseId));
        },
        getEnterButtonSelector() {
            return PUDONG_CONSTANTS.SELECTORS.ENTER_BTN;
        },
        getPlayerContainerSelector() {
            return PUDONG_CONSTANTS.SELECTORS.PLAYER_CONTAINER;
        }
    };
    const PudongPlayerFlow = {
        async startPlayerFlow() {
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "检测到课程播放页面,正在检索所有视频课件...",
                type: "info"
            });
            const courseId = this._extractCourseIdFromUrl();
            if (!courseId) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "无法从页面URL中提取课程ID",
                    type: "error"
                });
                return null;
            }
            const apiCourses = await PudongApi.getCoursewareList(courseId);
            if (apiCourses && apiCourses.length > 0) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: `成功获取到 ${apiCourses.length} 个视频课件`,
                    type: "success"
                });
                return apiCourses;
            }
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "无法通过API获取视频列表,处理当前单一视频",
                type: "warn"
            });
            return [ {
                id: courseId,
                courseId: courseId,
                title: document.title || `当前视频 ${courseId}`,
                courseName: document.title || `当前视频 ${courseId}`,
                durationStr: "00:30:00"
            } ];
        },
        _extractCourseIdFromUrl() {
            const urlParams = new URLSearchParams(window.location.search);
            let courseId = urlParams.get("id");
            if (!courseId) {
                const hash = window.location.hash;
                if (hash.includes("?")) {
                    const hashParams = new URLSearchParams(hash.split("?")[1]);
                    courseId = hashParams.get("id");
                }
                if (!courseId) {
                    const match = hash.match(/[?&]id=([^&]+)/);
                    if (match) {
                        courseId = match[1];
                    }
                }
            }
            return courseId;
        },
        async executeCourseLearning(course) {
            const {Utils: Utils} = await Promise.resolve().then(function() {
                return utils;
            });
            const {LearningStrategies: LearningStrategies} = await Promise.resolve().then(function() {
                return bizStrategies;
            });
            const courseId = course.id || course.courseId;
            const dsUnitId = course.dsUnitId;
            const playInfo = await PudongApi.getPlayInfo(courseId, dsUnitId, course.durationStr);
            if (!playInfo) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: `无法获取课程 ${courseId} 的播放信息`,
                    type: "error"
                });
                return false;
            }
            const courseInfo = {
                ...course,
                ...playInfo,
                title: course.title || course.courseName,
                courseId: courseId
            };
            const currentProgress = Math.floor(playInfo.lastLearnedTime / playInfo.duration * 100);
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `[学习启动] 课程: ${courseInfo.title}`,
                type: "info"
            });
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `[当前进度] ${currentProgress}% (${Utils.formatTime(playInfo.lastLearnedTime)}/${Utils.formatTime(playInfo.duration)})`,
                type: "info"
            });
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "执行极速完成策略 - 直接冲刺 99%",
                type: "info"
            });
            const success = await LearningStrategies.instant_finish({
                playInfo: courseInfo,
                duration: playInfo.duration,
                currentTime: playInfo.lastLearnedTime
            });
            if (success) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: `课程处理完成: ${courseInfo.title}`,
                    type: "success"
                });
            } else {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: `课程处理失败: ${courseInfo.title}`,
                    type: "error"
                });
            }
            return success;
        },
        async checkCourseCompletion(courseId, coursewareId = null) {
            return await PudongApi.checkCompletion(courseId, coursewareId);
        }
    };
    const PudongLearner$1 = {
        _validatePageType(pageType) {
            const allowedTypes = [ PudongHandler.PAGE_TYPES.PLAYER, PudongHandler.PAGE_TYPES.COLUMN, PudongHandler.PAGE_TYPES.INDEX ];
            if (allowedTypes.includes(pageType)) {
                return true;
            }
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "⚠️ 当前页面不支持自动学习。请进入课程播放页或列表页。",
                type: "warn"
            });
            EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, "页面不支持");
            return false;
        },
        async selectAndExecute() {
            if (!CONFIG$1.PUDONG_MODE) {
                return null;
            }
            const pageType = PudongHandler.identifyPage();
            if (!this._validatePageType(pageType)) {
                return false;
            }
            const href = window.location.href;
            if (pageType === PudongHandler.PAGE_TYPES.PLAYER || href.includes("/coursePlayer")) {
                return await this._handlePlayerPage();
            }
            if (pageType === PudongHandler.PAGE_TYPES.COLUMN || pageType === PudongHandler.PAGE_TYPES.INDEX || href.includes("/column") || href.includes("/index") || href.includes("specialdetail")) {
                return await this._handleColumnPage();
            }
            return null;
        },
        async _handlePlayerPage() {
            return await this.startPlayerFlow();
        },
        async _handleColumnPage() {
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "📋 检测到浦东分院专栏页",
                type: "info"
            });
            const courses = await PudongHandler.scanCourses();
            if (!courses || courses.length === 0) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "⚠️ 未扫描到课程列表",
                    type: "warn"
                });
                return false;
            }
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `✅ 扫描到 ${courses.length} 门课程`,
                type: "success"
            });
            const stats = {
                total: courses.length,
                completed: 0,
                learned: 0,
                failed: 0,
                skipped: 0
            };
            EventBus.publish(CONSTANTS.EVENTS.STATISTICS_UPDATE, stats);
            for (let i = 0; i < courses.length; i++) {
                const {Learner: Learner} = await Promise.resolve().then(function() {
                    return bizLearner;
                });
                if (Learner && Learner.stopRequested) {
                    EventBus.publish(CONSTANTS.EVENTS.LOG, {
                        message: "\n🛑 用户停止学习",
                        type: "warn"
                    });
                    break;
                }
                const course = courses[i];
                const courseId = course.id || course.courseId;
                const coursewareId = course.dsUnitId;
                EventBus.publish(CONSTANTS.EVENTS.COURSE_START, {
                    course: course,
                    index: i + 1,
                    total: courses.length
                });
                EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, `处理课程 ${i + 1}/${courses.length}`);
                try {
                    const completionCheck = await PudongHandler.checkCompletion(courseId, coursewareId);
                    if (completionCheck.isCompleted) {
                        EventBus.publish(CONSTANTS.EVENTS.LOG, {
                            message: `⏭️ 跳过已完成课程: ${course.title} (${completionCheck.finishedRate}%)`,
                            type: "info"
                        });
                        stats.skipped++;
                    } else {
                        const success = await PudongPlayerFlow.executeCourseLearning(course);
                        if (success) {
                            stats.learned++;
                        } else {
                            stats.failed++;
                        }
                    }
                    stats.completed = stats.skipped + stats.learned;
                    EventBus.publish(CONSTANTS.EVENTS.STATISTICS_UPDATE, stats);
                    EventBus.publish(CONSTANTS.EVENTS.PROGRESS_UPDATE, Math.floor((i + 1) / courses.length * 100));
                    if (stats.learned > 0 && i < courses.length - 1) {
                        await this._coolingDown(i === courses.length - 1);
                    }
                } catch (error) {
                    EventBus.publish(CONSTANTS.EVENTS.LOG, {
                        message: `❌ 处理课程失败: ${course.title} - ${error.message}`,
                        type: "error"
                    });
                    stats.failed++;
                    EventBus.publish(CONSTANTS.EVENTS.STATISTICS_UPDATE, stats);
                }
            }
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `\n🎉 所有课程处理完成!`,
                type: "success"
            });
            EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, "学习完成");
            this._resetToggleButton("学习完成");
            return true;
        },
        async startPlayerFlow() {
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "🎬 检测到浦东分院播放页,开始处理",
                type: "info"
            });
            const courses = await PudongPlayerFlow.startPlayerFlow();
            if (!courses || courses.length === 0) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "❌ 未找到可学习的课件",
                    type: "error"
                });
                return false;
            }
            const successCount = [];
            const failCount = [];
            for (const course of courses) {
                try {
                    const success = await PudongPlayerFlow.executeCourseLearning(course);
                    if (success) {
                        successCount.push(course);
                    } else {
                        failCount.push(course);
                    }
                } catch (error) {
                    console.error("[PudongLearner] 课件学习失败:", error);
                    failCount.push(course);
                }
            }
            if (successCount.length > 0) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: `✅ 成功完成 ${successCount.length} 个课件`,
                    type: "success"
                });
            }
            if (failCount.length > 0) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: `⚠️ ${failCount.length} 个课件处理失败`,
                    type: "warn"
                });
            }
            this._resetToggleButton("学习完成");
            return successCount.length > 0;
        },
        async _coolingDown(isLast) {
            if (isLast) return;
            const minDelay = 5e3;
            const maxDelay = 1e4;
            const delay = Math.random() * (maxDelay - minDelay) + minDelay;
            const seconds = Math.round(delay / 1e3);
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `冷却等待: ${seconds}秒`,
                type: "info"
            });
            for (let i = seconds; i > 0; i--) {
                EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, `等待中 (${i}s)`);
                await new Promise(r => setTimeout(r, 1e3));
            }
        },
        _resetToggleButton(statusText) {
            const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace("#", ""));
            if (toggleBtn) {
                toggleBtn.setAttribute("data-state", "stopped");
                toggleBtn.textContent = "开始学习";
            }
            EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, statusText);
        }
    };
    const PudongHandler = {
        PAGE_TYPES: PUDONG_CONSTANTS.PAGE_TYPES,
        SELECTORS: PUDONG_CONSTANTS.SELECTORS,
        identifyPage() {
            const url = window.location.href;
            if (url.includes(PUDONG_CONSTANTS.PATH_PATTERNS.PLAYER)) {
                return this.PAGE_TYPES.PLAYER;
            }
            for (const pattern of PUDONG_CONSTANTS.PATH_PATTERNS.COLUMN) {
                if (url.includes(pattern)) {
                    return this.PAGE_TYPES.COLUMN;
                }
            }
            if (url.includes(PUDONG_CONSTANTS.PATH_PATTERNS.INDEX)) {
                return this.PAGE_TYPES.INDEX;
            }
            return this.PAGE_TYPES.UNKNOWN;
        },
        init() {
            if (!this.isPudongMode()) return;
            console.log("浦东分院处理器已激活");
            EventBus.subscribe("pudong:startLearning", async () => {
                console.log("[PudongHandler] 收到开始学习事件");
                const pageType = this.identifyPage();
                const url = window.location.href;
                if (pageType === this.PAGE_TYPES.PLAYER) {
                    await PudongLearner$1._handlePlayerPage();
                } else if (pageType === this.PAGE_TYPES.COLUMN) {
                    if (url.includes("/specialcolumn")) {
                        EventBus.publish(CONSTANTS.EVENTS.LOG, {
                            message: "⚠️ 当前页面为专题入口页,请进入具体的专题详情页后再开始学习",
                            type: "warn"
                        });
                        EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, "页面不支持");
                        this._resetToggleButton();
                    } else if (url.includes("/pdchanel/pdzq")) {
                        EventBus.publish(CONSTANTS.EVENTS.LOG, {
                            message: "⚠️ 当前页面为干部履职通识课程专区,请进入具体的课程后再开始学习",
                            type: "warn"
                        });
                        EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, "页面不支持");
                        this._resetToggleButton();
                    } else {
                        await PudongLearner$1._handleColumnPage();
                    }
                } else if (pageType === this.PAGE_TYPES.INDEX) {
                    EventBus.publish(CONSTANTS.EVENTS.LOG, {
                        message: "⚠️ 首页暂不支持自动学习,请进入专栏或课程页面",
                        type: "warn"
                    });
                    this._resetToggleButton();
                } else {
                    EventBus.publish(CONSTANTS.EVENTS.LOG, {
                        message: "⚠️ 当前页面不支持自动学习",
                        type: "warn"
                    });
                    EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, "页面不支持");
                    this._resetToggleButton();
                }
            });
        },
        _resetToggleButton() {
            const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace("#", ""));
            if (toggleBtn) {
                toggleBtn.setAttribute("data-state", "stopped");
                toggleBtn.textContent = "开始学习";
            }
        },
        isPudongMode() {
            return CONFIG$1.PUDONG_MODE === true;
        },
        async scanCourses() {
            return await PudongScanner.scanCourses();
        },
        async startPlayerFlow() {
            return await PudongPlayerFlow.startPlayerFlow();
        },
        async executeCourseLearning(course) {
            return await PudongPlayerFlow.executeCourseLearning(course);
        },
        async checkCompletion(courseId, coursewareId = null) {
            return await PudongPlayerFlow.checkCourseCompletion(courseId, coursewareId);
        },
        async getPlayInfo(courseId, coursewareId = null, duration = null) {
            return await PudongApi.getPlayInfo(courseId, coursewareId, duration);
        },
        async reportProgress(playInfo, currentTime) {
            return await PudongApi.reportProgress(playInfo, currentTime);
        },
        getEnterButtonSelector() {
            return PudongScanner.getEnterButtonSelector();
        },
        getPlayerContainerSelector() {
            return PudongScanner.getPlayerContainerSelector();
        }
    };
    const CBEAD_CONSTANTS = {
        TIMEOUT: {
            NAVIGATION: 3e3,
            PAGE_LOAD: 1e4,
            PLAYER_LOAD: 15e3,
            LEARNING_SESSION: 6e5,
            POLLING_INTERVAL: 500
        },
        TIMING: {
            NAVIGATION: 3e3,
            PLAYER_INIT_TIMEOUT: 1e4,
            PLAYER_CHECK_INTERVAL: 500,
            PROGRESS_REPORT_INTERVAL: 3e4,
            CHAPTER_CHECK_INTERVAL: 15e3,
            MUTE_VERIFY_DELAY: 1e3,
            NEXT_COURSE_DELAY: 2e3,
            CHAPTER_SWITCH_DELAY: 2e3,
            PROTECTION_TIMEOUT: 100
        },
        THRESHOLDS: {
            COMPLETED_PROGRESS: 100,
            SKIP_COURSE_PROGRESS: 100,
            CHAPTER_CHECK_MIN_PROGRESS: 80,
            VIDEO_START_THRESHOLD: 10,
            STORAGE_EXPIRY: 864e5
        },
        STORAGE_KEYS: {
            LEARNING_PROGRESS: "cbead_learning_progress"
        },
        PATH_PATTERNS: {
            PLAYER: "study/course/detail",
            COLUMN: "train-new/class-detail",
            BRANCH_LIST: "branch-list-v",
            INDEX: "class/index",
            HOME_V: "home-v"
        },
        SELECTORS: {
            START_BUTTON: '.start-study-btn, [class*="start-learn"]',
            PROGRESS_BAR: ".el-progress-bar",
            VIDEO_PLAYER: 'video, [class*="video-player"], [class*="player-container"]',
            LEARNING_COMPLETED: '[class*="completed"], [class*="finished"]'
        },
        BRANCH_LIST: {
            CONTAINER_SELECTOR: ".card-wrapper",
            ITEM_SELECTOR: ".card-item",
            CARD_BOX_SELECTOR: ".card-box",
            STATUS_SELECTOR: ".status",
            TITLE_SELECTOR: ".title-row",
            PAGINATION_BOX_SELECTOR: ".e-pagination-box",
            PAGINATION_SELECTOR: ".zxy-pagination",
            PAGE_ITEM_SELECTOR: ".zxy-pagination-item:not(.zxy-pagination-prev):not(.zxy-pagination-next):not(.zxy-pagination-dots)",
            ACTIVE_PAGE_SELECTOR: ".zxy-pagination-item.active",
            NEXT_BTN_SELECTOR: ".e-pagination-box .zxy-pagination .zxy-pagination-next",
            PREV_BTN_SELECTOR: ".zxy-pagination-prev",
            DISABLED_PAGINATION_CLASS: "zxy-pagination-disabled",
            TAG_CONTAINER_SELECTOR: ".label-container .tag-list",
            TAG_BTN_SELECTOR: ".label-btn",
            CATEGORY_MENU_SELECTOR: ".menu-wrapper .catalog-menu",
            CATEGORY_ITEM_SELECTOR: ".catalog-menu-item",
            PAGE_LOAD_TIMEOUT: 5e3,
            PAGINATION_DELAY: 2e3,
            TAG_SWITCH_DELAY: 2e3,
            API_ENDPOINTS: {
                COURSE_LIST: "/api/v1/audience/course/list",
                MY_COURSE_LIST: "/api/v1/audience/course/my-course-list",
                STUDY_PROGRESS: "/api/v1/audience/course/study-progress"
            },
            VALIDATION: {
                PAGE_LOAD_TIMEOUT: 1e4,
                SKELETON_TIMEOUT: 5e3,
                CONTENT_TIMEOUT: 8e3,
                VUE_TIMEOUT: 6e3,
                MAX_RETRY_COUNT: 3
            }
        },
        COLUMN_PAGE: {
            ACTIVITY_STYLE: {
                CONTAINER_SELECTOR: ".activity-page",
                ITEM_SELECTOR: "li.clearfix",
                TITLE_SELECTOR: ".common-title",
                PROGRESS_BAR_SELECTOR: ".completed-rate-bar .bar",
                STUDY_BTN_SELECTOR: ".study-btn",
                COMPLETED_STATUS_TEXT: "已完成",
                IN_PROGRESS_STATUS_TEXT: "学习中",
                NOT_STARTED_STATUS_TEXT: "未开始"
            },
            LAYOUT_STYLE: {
                CONTAINER_SELECTOR: ".class-layout",
                LIST_SELECTOR: ".item-content ul.clearfix",
                ITEM_SELECTOR: "li.pull-left",
                TITLE_SELECTOR: ".title-text",
                TITLE_ID_PREFIX: "D74itemDetail-",
                STATUS_SELECTOR: ".ms-train-state",
                STATUS_TEXT: {
                    COMPLETED: "已完成",
                    UNFINISHED: "未完成",
                    IN_PROGRESS: "学习中"
                },
                TAB_SWITCH: {
                    CONTAINER_SELECTOR: ".item-switch-list",
                    TAB_ITEM_SELECTOR: "ul.clearfix > li",
                    ACTIVE_CLASS: "current",
                    SWITCH_DELAY: 1500
                }
            }
        },
        HEARTBEAT: {
            INTERVAL: 1e4,
            ENABLED: true,
            MAX_RETRIES: 3,
            TIMEOUT: 5e3,
            API_PENDING: true
        }
    };
    Object.defineProperty(CBEAD_CONSTANTS.COLUMN_PAGE, "CONTAINER_SELECTOR", {
        get() {
            return this.ACTIVITY_STYLE.CONTAINER_SELECTOR;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(CBEAD_CONSTANTS.COLUMN_PAGE, "ITEM_SELECTOR", {
        get() {
            return this.ACTIVITY_STYLE.ITEM_SELECTOR;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(CBEAD_CONSTANTS.COLUMN_PAGE, "PROGRESS_BAR_SELECTOR", {
        get() {
            return this.ACTIVITY_STYLE.PROGRESS_BAR_SELECTOR;
        },
        enumerable: true,
        configurable: true
    });
    const CourseStatusDetector = {
        STATUS: {
            COMPLETED: "completed",
            IN_PROGRESS: "in_progress",
            NOT_STARTED: "not_started"
        },
        STATUS_TEXT_MAP: {
            "学习中": "in_progress",
            "已完成": "completed",
            "未开始": "not_started"
        },
        detectCourseStatus(courseItem) {
            if (!courseItem || typeof courseItem.querySelector !== "function") {
                return this._createStatusResult(null, 0, "无效的课程元素");
            }
            const statusFromVue = this._detectFromVueData(courseItem);
            const statusFromText = this._detectFromStatusElement(courseItem);
            const statusFromProgressBar = this._detectFromProgressBar(courseItem);
            const statusFromClass = this._detectFromClass(courseItem);
            const statusFromDataAttr = this._detectFromDataAttr(courseItem);
            const results = [ statusFromVue, statusFromText, statusFromProgressBar, statusFromClass, statusFromDataAttr ];
            const validResults = results.filter(r => r !== null);
            const statusCounts = {};
            validResults.forEach(r => {
                statusCounts[r] = (statusCounts[r] || 0) + 1;
            });
            let finalStatus = null;
            let maxCount = 0;
            for (const [status, count] of Object.entries(statusCounts)) {
                if (count > maxCount) {
                    maxCount = count;
                    finalStatus = status;
                }
            }
            const confidence = this._calculateConfidence(statusFromVue, statusFromText, statusFromProgressBar, statusFromClass, statusFromDataAttr);
            const title = this._extractTitle(courseItem);
            const courseId = this._extractCourseId(courseItem);
            if (finalStatus) {
                console.log(`[CourseStatusDetector] ${title} - 状态: ${finalStatus}, 置信度: ${confidence}%, 来源: ${validResults.join(", ")}`);
            }
            return this._createStatusResult(finalStatus, confidence, {
                vue: statusFromVue,
                text: statusFromText,
                progressBar: statusFromProgressBar,
                class: statusFromClass,
                dataAttr: statusFromDataAttr
            }, {
                title: title,
                courseId: courseId
            });
        },
        detectBatch(courseItems) {
            if (!Array.isArray(courseItems)) {
                console.warn("[CourseStatusDetector] 批量检测收到非数组参数");
                return [];
            }
            return courseItems.map((item, index) => {
                const statusInfo = this.detectCourseStatus(item);
                return {
                    index: index,
                    element: item,
                    ...statusInfo
                };
            });
        },
        _detectFromVueData(courseItem) {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const cardItem = courseItem.querySelector(config.CARD_SELECTOR);
            if (!cardItem || !cardItem.__vue__) {
                return null;
            }
            const vueInstance = cardItem.__vue__;
            const data = vueInstance.$data || vueInstance._data || {};
            if (data.studyStatus !== undefined) {
                if (data.studyStatus === 1 || data.studyStatus === "studying") {
                    return this.STATUS.IN_PROGRESS;
                } else if (data.studyStatus === 2 || data.studyStatus === "completed") {
                    return this.STATUS.COMPLETED;
                } else if (data.studyStatus === 0 || data.studyStatus === "not_started") {
                    return this.STATUS.NOT_STARTED;
                }
            }
            const progress = data.studyProgress ?? data.progress ?? data.percentage ?? data.studyPercentage;
            if (progress !== undefined) {
                if (progress >= 100) {
                    return this.STATUS.COMPLETED;
                } else if (progress > 0) {
                    return this.STATUS.IN_PROGRESS;
                } else {
                    return this.STATUS.NOT_STARTED;
                }
            }
            if (data.isCompleted === true || data.isCompleted === "true") {
                return this.STATUS.COMPLETED;
            }
            if (data.completedTime || data.finishTime || data.studyEndTime) {
                return this.STATUS.COMPLETED;
            }
            return null;
        },
        _detectFromStatusElement(courseItem) {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const statusEl = courseItem.querySelector(config.STATUS_SELECTOR);
            if (!statusEl) {
                return null;
            }
            const statusText = statusEl.textContent?.trim() || "";
            if (statusText === "学习中") {
                return this.STATUS.IN_PROGRESS;
            } else if (statusText === "已完成") {
                return this.STATUS.COMPLETED;
            } else if (statusText === "未开始") {
                return this.STATUS.NOT_STARTED;
            }
            const hiddenStatus = statusEl.getAttribute("data-status") || statusEl.getAttribute("data-study-status");
            if (hiddenStatus) {
                if (hiddenStatus === "studying" || hiddenStatus === "1") {
                    return this.STATUS.IN_PROGRESS;
                } else if (hiddenStatus === "completed" || hiddenStatus === "2") {
                    return this.STATUS.COMPLETED;
                } else if (hiddenStatus === "not_started" || hiddenStatus === "0") {
                    return this.STATUS.NOT_STARTED;
                }
            }
            return null;
        },
        _detectFromProgressBar(courseItem) {
            const progressBar = courseItem.querySelector('.progress-bar, [class*="progress"], .completed-rate-bar, .rate-bar');
            if (!progressBar) {
                return null;
            }
            const style = progressBar.getAttribute("style") || "";
            const progressMatch = style.match(/width:\s*(\d+)%/);
            if (progressMatch) {
                const progress = parseInt(progressMatch[1], 10);
                if (progress >= 100) {
                    return this.STATUS.COMPLETED;
                } else if (progress > 0) {
                    return this.STATUS.IN_PROGRESS;
                }
            }
            const progressChild = progressBar.querySelector('.progress, .bar, .completed, [class*="completed"]');
            if (progressChild) {
                const childStyle = progressChild.getAttribute("style") || "";
                const childProgressMatch = childStyle.match(/width:\s*(\d+)%/);
                if (childProgressMatch) {
                    const progress = parseInt(childProgressMatch[1], 10);
                    if (progress >= 100) {
                        return this.STATUS.COMPLETED;
                    } else if (progress > 0) {
                        return this.STATUS.IN_PROGRESS;
                    }
                }
            }
            const progressText = progressBar.textContent || "";
            if (progressText.includes("100%") || progressText.includes("已完成")) {
                return this.STATUS.COMPLETED;
            }
            return null;
        },
        _detectFromClass(courseItem) {
            const classList = courseItem.classList;
            if (classList.contains("is-completed") || classList.contains("completed") || classList.contains("study-completed") || classList.contains("finished")) {
                return this.STATUS.COMPLETED;
            }
            if (classList.contains("is-learning") || classList.contains("learning") || classList.contains("study-in-progress") || classList.contains("in-study")) {
                return this.STATUS.IN_PROGRESS;
            }
            if (classList.contains("is-not-started") || classList.contains("not-started") || classList.contains("unstarted")) {
                return this.STATUS.NOT_STARTED;
            }
            return null;
        },
        _detectFromDataAttr(courseItem) {
            const dataStatus = courseItem.getAttribute("data-status") || courseItem.getAttribute("data-study-status") || courseItem.getAttribute("data-progress-status");
            if (dataStatus) {
                if (dataStatus === "completed" || dataStatus === "2" || dataStatus === "true") {
                    return this.STATUS.COMPLETED;
                } else if (dataStatus === "studying" || dataStatus === "learning" || dataStatus === "1") {
                    return this.STATUS.IN_PROGRESS;
                } else if (dataStatus === "not_started" || dataStatus === "0" || dataStatus === "false") {
                    return this.STATUS.NOT_STARTED;
                }
            }
            const dataProgress = courseItem.getAttribute("data-progress");
            if (dataProgress !== null) {
                const progress = parseInt(dataProgress, 10);
                if (!isNaN(progress)) {
                    if (progress >= 100) {
                        return this.STATUS.COMPLETED;
                    } else if (progress > 0) {
                        return this.STATUS.IN_PROGRESS;
                    } else {
                        return this.STATUS.NOT_STARTED;
                    }
                }
            }
            return null;
        },
        _calculateConfidence(vue, text, progressBar, className, dataAttr) {
            let score = 0;
            let sources = 0;
            const weights = {
                vue: 35,
                text: 25,
                progressBar: 20,
                dataAttr: 15,
                class: 5
            };
            if (vue) {
                score += weights.vue;
                sources++;
            }
            if (text) {
                score += weights.text;
                sources++;
            }
            if (progressBar) {
                score += weights.progressBar;
                sources++;
            }
            if (dataAttr) {
                score += weights.dataAttr;
                sources++;
            }
            if (className) {
                score += weights.class;
                sources++;
            }
            if (sources >= 3) {
                score += 15;
            } else if (sources >= 2) {
                score += 8;
            }
            if (sources === 1) {
                score *= .8;
            }
            return Math.min(Math.round(score), 100);
        },
        _createStatusResult(status, confidence, sources = {}, extra = {}) {
            return {
                status: status,
                confidence: confidence,
                sources: sources,
                isCompleted: status === this.STATUS.COMPLETED,
                isInProgress: status === this.STATUS.IN_PROGRESS,
                isNotStarted: status === this.STATUS.NOT_STARTED,
                ...extra
            };
        },
        _extractTitle(courseItem) {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const titleEl = courseItem.querySelector(config.TITLE_SELECTOR);
            return titleEl?.textContent?.trim() || "未知课程";
        },
        _extractCourseId(courseItem) {
            const linkEl = courseItem.querySelector('a[href*="/study/course/detail/"]');
            if (linkEl) {
                const href = linkEl.getAttribute("href");
                const match = href.match(/detail\/([^&\/]+)/);
                if (match) return match[1];
            }
            const dataId = courseItem.getAttribute("data-id") || courseItem.getAttribute("data-course-id");
            if (dataId) return dataId;
            return null;
        },
        filterLearningNeeded(statusResults) {
            return statusResults.filter(result => {
                if (result.status === this.STATUS.COMPLETED && result.confidence >= 70) {
                    return false;
                }
                return result.status === this.STATUS.IN_PROGRESS || result.status === this.STATUS.NOT_STARTED;
            });
        },
        getStatistics(statusResults) {
            const stats = {
                total: statusResults.length,
                completed: 0,
                inProgress: 0,
                notStarted: 0,
                unknown: 0,
                avgConfidence: 0
            };
            let totalConfidence = 0;
            let confidenceCount = 0;
            statusResults.forEach(result => {
                if (result.status === this.STATUS.COMPLETED) {
                    stats.completed++;
                } else if (result.status === this.STATUS.IN_PROGRESS) {
                    stats.inProgress++;
                } else if (result.status === this.STATUS.NOT_STARTED) {
                    stats.notStarted++;
                } else {
                    stats.unknown++;
                }
                if (result.confidence > 0) {
                    totalConfidence += result.confidence;
                    confidenceCount++;
                }
            });
            stats.avgConfidence = confidenceCount > 0 ? Math.round(totalConfidence / confidenceCount) : 0;
            return stats;
        }
    };
    const BranchListValidator = {
        async validateAndGetInfo() {
            const urlValidation = this._validateUrlFormat();
            if (!urlValidation.isValid) {
                return {
                    isValid: false,
                    pageType: "unknown",
                    pageNumber: 1,
                    totalPages: 1,
                    courseCount: 0,
                    error: urlValidation.reason
                };
            }
            const domValidation = this._validateDomElements();
            if (!domValidation.isValid) {
                return {
                    isValid: false,
                    pageType: "unknown",
                    pageNumber: 1,
                    totalPages: 1,
                    courseCount: 0,
                    error: domValidation.reason
                };
            }
            const pageReady = await this._waitForPageReady();
            if (!pageReady) {
                console.warn(`[BranchListValidator] 页面加载超时,尝试提取基本信息`);
            }
            const pageInfo = await this.extractPageInfo();
            return {
                isValid: true,
                pageType: "branch-list-v",
                organizationId: pageInfo.organizationId,
                pageNumber: pageInfo.currentPage,
                totalPages: pageInfo.totalPages,
                courseCount: pageInfo.courseCount,
                pageSize: pageInfo.pageSize,
                ...pageInfo
            };
        },
        async validatePage() {
            const urlValidation = this._validateUrlFormat();
            if (!urlValidation.isValid) {
                console.warn(`[BranchListValidator] URL格式验证失败: ${urlValidation.reason}`);
                return {
                    isValid: false,
                    pageInfo: null,
                    error: urlValidation.reason
                };
            }
            const domValidation = this._validateDomElements();
            if (!domValidation.isValid) {
                console.warn(`[BranchListValidator] DOM元素验证失败: ${domValidation.reason}`);
                return {
                    isValid: false,
                    pageInfo: null,
                    error: domValidation.reason
                };
            }
            const pageReady = await this._waitForPageReady();
            if (!pageReady) {
                console.warn(`[BranchListValidator] 页面加载超时`);
                return {
                    isValid: false,
                    pageInfo: null,
                    error: "页面加载超时"
                };
            }
            const pageInfo = await this.extractPageInfo();
            console.log(`[BranchListValidator] 页面验证通过`);
            console.log(`[BranchListValidator] 组织ID: ${pageInfo.organizationId.substring(0, 8)}...`);
            console.log(`[BranchListValidator] 总页数: ${pageInfo.totalPages}`);
            console.log(`[BranchListValidator] 当前页: ${pageInfo.currentPage}`);
            console.log(`[BranchListValidator] 课程数量: ${pageInfo.courseCount}`);
            return {
                isValid: true,
                pageInfo: pageInfo
            };
        },
        _validateUrlFormat() {
            const hash = window.location.hash || "";
            if (!hash.includes("#/branch-list-v/")) {
                return {
                    isValid: false,
                    reason: "URL不包含 branch-list-v 路径"
                };
            }
            const match = hash.match(/#\/branch-list-v\/([a-f0-9-]+)/i);
            if (!match) {
                return {
                    isValid: false,
                    reason: "无法提取组织ID"
                };
            }
            const organizationId = match[1];
            if (!/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(organizationId)) {
                return {
                    isValid: false,
                    reason: "组织ID格式无效"
                };
            }
            return {
                isValid: true,
                organizationId: organizationId
            };
        },
        _validateDomElements() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const container = document.querySelector(config.CONTAINER_SELECTOR);
            if (!container) {
                return {
                    isValid: false,
                    reason: `未找到课程列表容器: ${config.CONTAINER_SELECTOR}`
                };
            }
            const pagination = document.querySelector(config.PAGINATION_SELECTOR);
            if (!pagination) {
                return {
                    isValid: false,
                    reason: `未找到分页组件: ${config.PAGINATION_SELECTOR}`
                };
            }
            return {
                isValid: true,
                container: container,
                pagination: pagination
            };
        },
        async _waitForPageReady() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const validationConfig = config.VALIDATION || {
                PAGE_LOAD_TIMEOUT: 1e4
            };
            console.log(`[BranchListValidator] 等待页面加载...`);
            let pageLoaded = false;
            let attempts = 0;
            const maxAttempts = Math.ceil(validationConfig.PAGE_LOAD_TIMEOUT / 500);
            while (!pageLoaded && attempts < maxAttempts) {
                const container = document.querySelector(config.CONTAINER_SELECTOR);
                if (container) {
                    const items = container.querySelectorAll(config.ITEM_SELECTOR);
                    if (items && items.length > 0) {
                        pageLoaded = true;
                        console.log(`[BranchListValidator] ✅ 页面已就绪,检测到 ${items.length} 个课程项`);
                    }
                }
                if (!pageLoaded) {
                    await new Promise(resolve => setTimeout(resolve, 500));
                    attempts++;
                }
            }
            if (pageLoaded) {
                return true;
            }
            console.warn(`[BranchListValidator] 页面加载超时`);
            return false;
        },
        async extractPageInfo() {
            const urlMatch = window.location.hash.match(/#\/branch-list-v\/([a-f0-9-]+)/i);
            const organizationId = urlMatch ? urlMatch[1] : null;
            return {
                organizationId: organizationId,
                currentPage: this._getCurrentPage(),
                totalPages: this._getTotalPages(),
                courseCount: this._getCourseCount(),
                pageSize: this._getPageSize(),
                startTime: Date.now()
            };
        },
        _getCurrentPage() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const activeItem = document.querySelector(config.ACTIVE_PAGE_SELECTOR);
            if (activeItem) {
                const pageNum = parseInt(activeItem.textContent?.trim(), 10);
                if (!isNaN(pageNum) && pageNum > 0) {
                    return pageNum;
                }
            }
            const isActiveItem = document.querySelector(`${config.PAGINATION_SELECTOR} .is-active`);
            if (isActiveItem) {
                const pageNum = parseInt(isActiveItem.textContent?.trim(), 10);
                if (!isNaN(pageNum) && pageNum > 0) {
                    return pageNum;
                }
            }
            const activeLi = document.querySelector(`${config.PAGINATION_SELECTOR} li.active`);
            if (activeLi) {
                const pageNum = parseInt(activeLi.textContent?.trim(), 10);
                if (!isNaN(pageNum) && pageNum > 0) {
                    return pageNum;
                }
            }
            console.warn(`[BranchListValidator] 无法确定当前页码,默认为1`);
            return 1;
        },
        _getTotalPages() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const paginationBox = document.querySelector(config.PAGINATION_BOX_SELECTOR);
            if (paginationBox) {
                const pageText = paginationBox.textContent || "";
                const totalMatch = pageText.match(/共\s*(\d+)\s*页/);
                if (totalMatch) {
                    const total = parseInt(totalMatch[1], 10);
                    if (total > 0) {
                        return total;
                    }
                }
                const slashMatch = pageText.match(/(\d+)\s*\/\s*(\d+)/);
                if (slashMatch) {
                    const total = parseInt(slashMatch[2], 10);
                    if (total > 0) {
                        return total;
                    }
                }
            }
            const paginationEl = document.querySelector(config.PAGINATION_SELECTOR);
            if (paginationEl) {
                const pageItems = paginationEl.querySelectorAll(config.PAGE_ITEM_SELECTOR);
                if (pageItems.length > 0) {
                    const lastItem = pageItems[pageItems.length - 1];
                    const lastPageNum = parseInt(lastItem.textContent?.trim(), 10);
                    if (!isNaN(lastPageNum) && lastPageNum > 0) {
                        return lastPageNum;
                    }
                }
            }
            const lastPageBtn = document.querySelector(`${config.PAGINATION_SELECTOR} .zxy-pagination-item-last`);
            if (lastPageBtn) {
                const lastPage = lastPageBtn.getAttribute("data-page");
                if (lastPage) {
                    const total = parseInt(lastPage, 10);
                    if (total > 0) {
                        return total;
                    }
                }
            }
            console.warn(`[BranchListValidator] 无法确定总页数,默认为1`);
            return 1;
        },
        _getCourseCount() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const container = document.querySelector(config.CONTAINER_SELECTOR);
            if (!container) {
                return 0;
            }
            const courseItems = container.querySelectorAll(config.ITEM_SELECTOR);
            return courseItems.length;
        },
        _getPageSize() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const paginationBox = document.querySelector(config.PAGINATION_BOX_SELECTOR);
            if (paginationBox) {
                const pageText = paginationBox.textContent || "";
                const sizeMatch = pageText.match(/每页\s*(\d+)\s*条/);
                if (sizeMatch) {
                    return parseInt(sizeMatch[1], 10);
                }
            }
            const courseCount = this._getCourseCount();
            if (courseCount > 0 && courseCount <= 12) return 12;
            if (courseCount > 12 && courseCount <= 16) return 16;
            if (courseCount > 16 && courseCount <= 20) return 20;
            return 20;
        },
        isLastPage() {
            const currentPage = this._getCurrentPage();
            const totalPages = this._getTotalPages();
            return currentPage >= totalPages;
        },
        getPaginationSummary() {
            const currentPage = this._getCurrentPage();
            const totalPages = this._getTotalPages();
            const courseCount = this._getCourseCount();
            return `第 ${currentPage}/${totalPages} 页,共 ${courseCount} 门课程`;
        }
    };
    const CbeadScanner = {
        extractCourseInfo(element) {
            try {
                const titleSelectors = [ ".common-title", ".title", ".activity-stage-name", "h3", "h4" ];
                let courseName = null;
                for (const selector of titleSelectors) {
                    const titleEl = element.querySelector(selector);
                    if (titleEl) {
                        courseName = titleEl.textContent?.trim();
                        break;
                    }
                }
                if (!courseName) return null;
                const linkEl = element.querySelector("a");
                const courseId = linkEl?.getAttribute("data-id") || element?.getAttribute("data-id") || linkEl?.getAttribute("id");
                const studyLink = linkEl?.getAttribute("href") || element.querySelector(".study-btn")?.getAttribute("data-url");
                return {
                    id: courseId,
                    name: courseName,
                    link: studyLink,
                    element: element
                };
            } catch (error) {
                console.warn("[CbeadScanner] 提取课程信息失败:", error);
                return null;
            }
        },
        getCourses(selectors) {
            const courses = [];
            for (const selector of selectors.COURSE_ITEMS) {
                const elements = document.querySelectorAll(selector);
                for (const element of elements) {
                    const courseInfo = this.extractCourseInfo(element);
                    if (courseInfo) {
                        courses.push(courseInfo);
                    }
                }
                if (courses.length > 0) break;
            }
            return courses;
        },
        categorizeAndSortCourses(courses) {
            if (!Array.isArray(courses)) {
                console.warn("[CbeadScanner] categorizeAndSortCourses 收到非数组参数:", typeof courses);
                return {
                    inProgress: [],
                    notStarted: [],
                    completed: [],
                    toLearn: []
                };
            }
            const categorized = {
                inProgress: [],
                notStarted: [],
                completed: [],
                toLearn: []
            };
            courses.forEach(course => {
                if (course.status === "in_progress") {
                    categorized.inProgress.push(course);
                    categorized.toLearn.push(course);
                } else if (course.status === "not_started") {
                    categorized.notStarted.push(course);
                    categorized.toLearn.push(course);
                } else if (course.status === "completed") {
                    categorized.completed.push(course);
                }
            });
            categorized.inProgress.sort((a, b) => b.progress - a.progress);
            console.log(`[CbeadScanner] 课程分类结果:`);
            console.log(`  - 📖 学习中: ${categorized.inProgress.length} 门`);
            console.log(`  - 📝 未开始: ${categorized.notStarted.length} 门`);
            console.log(`  - ✅ 已完成: ${categorized.completed.length} 门 (将跳过)`);
            console.log(`  - 📚 需要学习: ${categorized.toLearn.length} 门`);
            return categorized;
        },
        getSortedLearningList(courses) {
            const categorized = this.categorizeAndSortCourses(courses);
            const sortedList = [ ...categorized.inProgress, ...categorized.notStarted ];
            console.log(`[CbeadScanner] 学习顺序:`);
            sortedList.forEach((course, index) => {
                let prefix = course.status === "in_progress" ? "📖" : "📝";
                console.log(`  ${index + 1}. ${prefix} ${course.title} (${course.progress}%)`);
            });
            if (categorized.completed.length > 0) {
                console.log(`[CbeadScanner] 以下课程将跳过(已完成):`);
                categorized.completed.forEach(course => {
                    console.log(`  ✅ ${course.title} (${course.progress}%)`);
                });
            }
            return sortedList;
        },
        async scanCoursesFromBranchListPage() {
            const courses = [];
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            console.log(`[CbeadScanner] 开始扫描课程列表...`);
            console.log(`[CbeadScanner] 选择器配置:`);
            console.log(`  - 容器选择器: ${config.CONTAINER_SELECTOR}`);
            console.log(`  - 课程项选择器: ${config.ITEM_SELECTOR}`);
            console.log(`  - 卡片容器选择器: ${config.CARD_BOX_SELECTOR}`);
            console.log(`  - 标题选择器: ${config.TITLE_SELECTOR}`);
            console.log(`  - 状态选择器: ${config.STATUS_SELECTOR}`);
            let container = document.querySelector(config.CONTAINER_SELECTOR);
            let attempts = 0;
            const maxAttempts = 20;
            while (!container && attempts < maxAttempts) {
                await new Promise(resolve => setTimeout(resolve, 500));
                container = document.querySelector(config.CONTAINER_SELECTOR);
                attempts++;
            }
            if (attempts > 0) {
                console.log(`[CbeadScanner] 等待页面加载完成,尝试 ${attempts} 次`);
            }
            if (!container) {
                console.warn(`[CbeadScanner] ❌ 未找到课程列表容器: ${config.CONTAINER_SELECTOR}`);
                const altContainer = document.querySelector(".card-wrapper") || document.querySelector(".card-box");
                if (altContainer) {
                    console.log(`[CbeadScanner] ✅ 找到备选容器: ${altContainer.className}`);
                }
                return courses;
            }
            console.log(`[CbeadScanner] ✅ 找到课程列表容器`);
            const courseItems = container.querySelectorAll(config.ITEM_SELECTOR);
            console.log(`[CbeadScanner] 📚 使用 ${config.ITEM_SELECTOR} 找到 ${courseItems.length} 个课程项`);
            if (courseItems.length === 0) {
                console.log(`[CbeadScanner] 使用 ${config.ITEM_SELECTOR} 未找到课程项,尝试备选选择器...`);
                const altItems = container.querySelectorAll(config.CARD_BOX_SELECTOR);
                if (altItems.length > 0) {
                    console.log(`[CbeadScanner] ✅ 使用备选选择器 ${config.CARD_BOX_SELECTOR} 找到 ${altItems.length} 个课程项`);
                }
            }
            courseItems.forEach((item, index) => {
                try {
                    const courseInfo = this._extractCourseInfoFromBranchListItem(item, index);
                    if (courseInfo) {
                        courses.push(courseInfo);
                    }
                } catch (error) {
                    console.error(`[CbeadScanner] 处理课程项失败 (索引 ${index}):`, error);
                }
            });
            if (courses.length > 0) {
                console.log(`[CbeadScanner] 从DOM成功提取 ${courses.length} 门课程`);
                return courses;
            }
            const vueCourses = await this._extractCoursesFromVueData();
            if (vueCourses && vueCourses.length > 0) {
                console.log(`[CbeadScanner] 从Vue组件数据成功提取 ${vueCourses.length} 门课程`);
                return vueCourses;
            }
            const apiCourses = await this._fetchCoursesFromApi();
            if (apiCourses && apiCourses.length > 0) {
                console.log(`[CbeadScanner] 从API成功提取 ${apiCourses.length} 门课程`);
                return apiCourses;
            }
            const deepVueCourses = await this._extractFromVueDeepScan();
            if (deepVueCourses && deepVueCourses.length > 0) {
                console.log(`[CbeadScanner] 从Vue深度扫描成功提取 ${deepVueCourses.length} 门课程`);
                return deepVueCourses;
            }
            console.log(`[CbeadScanner] 成功扫描 ${courses.length} 门课程`);
            return courses;
        },
        async _fetchCoursesFromApi() {
            const courses = [];
            try {
                const hash = window.location.hash || "";
                const match = hash.match(/branch-list-v\/([a-f0-9-]+)/i);
                const organizationId = match ? match[1] : null;
                if (!organizationId) {
                    console.debug("[CbeadScanner] 无法从URL提取组织ID");
                    return courses;
                }
                console.log(`[CbeadScanner] 尝试从API获取课程列表,组织ID: ${organizationId.substring(0, 8)}...`);
                const apiUrl = `/api/v1/audience/course/list`;
                const response = await fetch(apiUrl, {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json"
                    },
                    body: JSON.stringify({
                        organizationId: organizationId,
                        pageNum: 1,
                        pageSize: 20
                    })
                });
                if (!response.ok) {
                    console.debug(`[CbeadScanner] API请求失败: ${response.status}`);
                    return courses;
                }
                const data = await response.json();
                console.log(`[CbeadScanner] API返回数据:`, data);
                if (data.code === 200 || data.code === 0 || data.success) {
                    const records = data.data?.records || data.records || data.list || data.data || [];
                    if (Array.isArray(records) && records.length > 0) {
                        for (const item of records) {
                            const courseInfo = this._buildCourseInfoFromApiData(item);
                            if (courseInfo && courseInfo.id) {
                                courses.push(courseInfo);
                            }
                        }
                        console.log(`[CbeadScanner] 从API解析到 ${courses.length} 门课程`);
                    }
                }
            } catch (error) {
                console.debug(`[CbeadScanner] 从API获取课程失败:`, error.message);
            }
            return courses;
        },
        _extractCoursesFromVueData() {
            const courses = [];
            const vueElements = document.querySelectorAll("[data-v-]");
            for (const el of vueElements) {
                if (el.__vue__) {
                    const vueInstance = el.__vue__;
                    const courseData = this._parseVueInstanceData(vueInstance);
                    if (courseData.length > 0) {
                        courses.push(...courseData);
                    }
                }
            }
            if (window.__VueApp__ || window.__vue__ || window.Vue) {
                try {
                    const vueApp = window.__VueApp__ || window.__vue__ || window.Vue;
                    if (vueApp && vueApp.componentInstances) {
                        for (const instance of vueApp.componentInstances) {
                            const courseData = this._parseVueInstanceData(instance);
                            if (courseData.length > 0) {
                                courses.push(...courseData);
                            }
                        }
                    }
                } catch (e) {
                    console.debug("[CbeadScanner] 无法访问全局Vue实例:", e);
                }
            }
            const cardItems = document.querySelectorAll(".card-item");
            for (const card of cardItems) {
                const parentVue = this._findParentVueInstance(card);
                if (parentVue) {
                    const courseData = this._parseVueInstanceData(parentVue);
                    if (courseData.length > 0) {
                        for (const course of courseData) {
                            if (!courses.find(c => c.id === course.id)) {
                                courses.push(course);
                            }
                        }
                    }
                }
            }
            return courses;
        },
        _findParentVueInstance(element) {
            let el = element;
            const maxDepth = 10;
            let depth = 0;
            while (el && depth < maxDepth) {
                if (el.__vue__) {
                    return el.__vue__;
                }
                if (el._vueParent) {
                    return el._vueParent;
                }
                el = el.parentElement;
                depth++;
            }
            return null;
        },
        _parseVueInstanceData(vueInstance) {
            const courses = [];
            if (!vueInstance) return courses;
            const dataProps = [ "courseList", "list", "courses", "items", "data", "courseData", "tableData", "tableList", "$data", "data", "propsData" ];
            for (const prop of dataProps) {
                try {
                    let data = vueInstance[prop];
                    if (!data && vueInstance.$data) {
                        data = vueInstance.$data[prop];
                    }
                    if (data && Array.isArray(data) && data.length > 0) {
                        const firstItem = data[0];
                        if (firstItem.id || firstItem.courseId || firstItem.dsUnitId || firstItem.courseName || firstItem.title) {
                            for (const item of data) {
                                const courseInfo = this._buildCourseInfoFromApiData(item);
                                if (courseInfo && courseInfo.id) {
                                    courses.push(courseInfo);
                                }
                            }
                            console.log(`[CbeadScanner] 从Vue数据属性 ${prop} 提取到 ${courses.length} 门课程`);
                            return courses;
                        }
                    }
                } catch (e) {}
            }
            try {
                const keys = Object.keys(vueInstance);
                for (const key of keys) {
                    if (key.startsWith("_") || key === "constructor") continue;
                    try {
                        const value = vueInstance[key];
                        if (value && typeof value === "object") {
                            if (Array.isArray(value)) {
                                const courseData = this._checkAndParseCourseArray(value);
                                if (courseData.length > 0) {
                                    courses.push(...courseData);
                                }
                            }
                            if (value.data && Array.isArray(value.data)) {
                                const courseData = this._checkAndParseCourseArray(value.data);
                                if (courseData.length > 0) {
                                    courses.push(...courseData);
                                }
                            }
                            if (value.list && Array.isArray(value.list)) {
                                const courseData = this._checkAndParseCourseArray(value.list);
                                if (courseData.length > 0) {
                                    courses.push(...courseData);
                                }
                            }
                        }
                    } catch (e) {}
                }
            } catch (e) {
                console.debug("[CbeadScanner] 解析Vue实例数据失败:", e);
            }
            return courses;
        },
        _checkAndParseCourseArray(arr) {
            const courses = [];
            if (!arr || arr.length === 0) return courses;
            const firstItem = arr[0];
            const hasCourseFields = firstItem.id || firstItem.courseId || firstItem.dsUnitId || firstItem.courseName || firstItem.title || firstItem.name;
            if (hasCourseFields) {
                for (const item of arr) {
                    const courseInfo = this._buildCourseInfoFromApiData(item);
                    if (courseInfo && courseInfo.id) {
                        courses.push(courseInfo);
                    }
                }
            }
            return courses;
        },
        _buildCourseInfoFromApiData(data) {
            const courseId = data.id || data.courseId || data.dsUnitId || null;
            if (!courseId) return null;
            const title = data.courseName || data.title || data.name || "未知课程";
            const studyLink = `#/study/course/detail/${courseId}`;
            let progress = 0;
            let status = "not_started";
            if (data.studyProgress !== undefined) {
                progress = data.studyProgress;
            } else if (data.progress !== undefined) {
                progress = data.progress;
            } else if (data.percentage !== undefined) {
                progress = data.percentage;
            }
            if (progress >= CBEAD_CONSTANTS.THRESHOLDS.COMPLETED_PROGRESS) {
                status = "completed";
            } else if (progress > 0) {
                status = "in_progress";
            }
            if (data.studyStatus === 1 || data.studyStatus === "studying") {
                status = "in_progress";
                progress = progress || 50;
            } else if (data.studyStatus === 2 || data.studyStatus === "completed") {
                status = "completed";
                progress = 100;
            } else if (data.studyStatus === 0 || data.studyStatus === "not_started") {
                status = "not_started";
                progress = 0;
            }
            return {
                id: courseId,
                courseId: courseId,
                dsUnitId: courseId,
                title: title,
                courseName: title,
                link: studyLink,
                progress: progress,
                isCompleted: progress >= CBEAD_CONSTANTS.THRESHOLDS.COMPLETED_PROGRESS,
                status: status,
                element: null,
                source: "cbead_vue_data_scan",
                rawData: data
            };
        },
        _extractCourseInfoFromBranchListItem(item, index) {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const titleEl = item.querySelector(config.TITLE_SELECTOR);
            const title = titleEl?.textContent?.trim() || null;
            if (!title) {
                console.warn(`[CbeadScanner] 无法提取课程标题 (索引 ${index})`);
                return null;
            }
            let courseId = null;
            let studyLink = null;
            courseId = item.getAttribute("data-id") || item.getAttribute("data-course-id") || item.getAttribute("data-key") || item.getAttribute("data-vid") || item.getAttribute("data-idx");
            if (!courseId && item.id) {
                const idMatch = item.id.match(/([a-f0-9-]{36}|[a-f0-9]{32})/i);
                if (idMatch) courseId = idMatch[1];
            }
            if (!courseId) {
                const dataIdEl = item.querySelector("[data-id], [data-course-id]");
                if (dataIdEl) {
                    courseId = dataIdEl.getAttribute("data-id") || dataIdEl.getAttribute("data-course-id");
                }
            }
            if (!studyLink) {
                const linkEl = item.querySelector('a[href*="/study/course/detail/"]');
                if (linkEl) {
                    const href = linkEl.getAttribute("href");
                    if (href && href.includes("/study/course/detail/")) {
                        studyLink = href;
                        if (!courseId) {
                            const linkMatch = href.match(/detail\/([^&\/]+)/);
                            if (linkMatch) courseId = linkMatch[1];
                        }
                    }
                }
            }
            if (!courseId || !studyLink) {
                const vueData = this._scanVueInstanceForCourseData(item, title);
                if (vueData) {
                    if (!courseId && vueData.id) courseId = vueData.id;
                    if (!studyLink && vueData.link) studyLink = vueData.link;
                }
            }
            if (!courseId) {
                const dynamicKey = item.getAttribute("data-dynamic-key") || item.querySelector("[data-dynamic-key]")?.getAttribute("data-dynamic-key");
                if (dynamicKey && /^[a-f0-9-]{36}$/i.test(dynamicKey)) {
                    courseId = dynamicKey;
                }
            }
            if (!courseId || !studyLink) {
                const clickEl = item.querySelector("[onclick], [data-link], [data-url]");
                if (clickEl) {
                    const onclick = clickEl.getAttribute("onclick") || clickEl.getAttribute("data-link") || clickEl.getAttribute("data-url");
                    if (onclick) {
                        const linkMatch = onclick.match(/['"]?([^'"]*\/study\/course\/detail\/[^'"]*)['"]?/) || onclick.match(/detail\/([^&\/]+)/);
                        if (linkMatch) {
                            const link = linkMatch[1] || linkMatch[0];
                            studyLink = link.includes("#") ? link : `#/study/course/detail/${link}`;
                            if (!courseId) {
                                const idMatch = link.match(/detail\/([^&\/]+)/);
                                if (idMatch) courseId = idMatch[1];
                            }
                        }
                    }
                }
            }
            if (courseId && !studyLink) {
                studyLink = `#/study/course/detail/${courseId}`;
            }
            const statusEl = item.querySelector(config.STATUS_SELECTOR);
            const statusText = statusEl?.textContent?.trim() || "";
            let progress = 0;
            let status = "not_started";
            if (statusText === "学习中") {
                status = "in_progress";
                progress = 50;
            } else if (statusText === "已完成") {
                status = "completed";
                progress = 100;
            } else if (statusText === "未开始") {
                status = "not_started";
                progress = 0;
            }
            const progressBar = item.querySelector('.progress-bar, .completed-rate-bar, [class*="progress"]');
            if (progressBar) {
                const style = progressBar.getAttribute("style") || "";
                const progressMatch = style.match(/width:\s*(\d+)%/);
                if (progressMatch) {
                    progress = parseInt(progressMatch[1], 10);
                    if (progress >= CBEAD_CONSTANTS.THRESHOLDS.COMPLETED_PROGRESS) {
                        status = "completed";
                    } else if (progress > 0) {
                        status = "in_progress";
                    }
                }
            }
            const courseInfo = {
                id: courseId,
                courseId: courseId,
                dsUnitId: courseId,
                title: title,
                courseName: title,
                link: studyLink,
                element: item,
                progress: progress,
                isCompleted: progress >= CBEAD_CONSTANTS.THRESHOLDS.COMPLETED_PROGRESS,
                status: status,
                statusText: statusText,
                source: "cbead_branch_list_scan"
            };
            const statusTextDisplay = {
                completed: "✅ 已完成",
                in_progress: "📖 学习中",
                not_started: "📝 未开始"
            };
            const idDisplay = courseId ? `[ID:${courseId.substring(0, 8)}...]` : "[无ID]";
            const linkDisplay = studyLink ? `[链接:${studyLink}]` : "[无链接]";
            console.log(`[CbeadScanner] 课程 ${index + 1}: ${title} - ${statusTextDisplay[status]} (${progress}%) ${idDisplay} ${linkDisplay}`);
            return courseInfo;
        },
        _scanVueInstanceForCourseData(element, title) {
            const vueInstances = [];
            let el = element;
            for (let depth = 0; depth < 15 && el; depth++) {
                if (el.__vue__) {
                    vueInstances.push(el.__vue__);
                }
                if (el.__vue__) {
                    const vm = el.__vue__;
                    if (vm.$root && vm.$root !== vm && !vueInstances.includes(vm.$root)) {
                        vueInstances.push(vm.$root);
                    }
                    if (vm.$parent && vm.$parent !== vm && !vueInstances.includes(vm.$parent)) {
                        vueInstances.push(vm.$parent);
                    }
                }
                el = el.parentElement;
            }
            for (const vm of vueInstances) {
                try {
                    const result = this._scanVueForCourse(vm, title);
                    if (result) return result;
                } catch (e) {}
            }
            const dataVElements = element.querySelectorAll("[data-v-]");
            for (const el of dataVElements) {
                if (el.__vue__) {
                    try {
                        const result = this._scanVueForCourse(el.__vue__, title);
                        if (result) return result;
                    } catch (e) {}
                }
            }
            return null;
        },
        _scanVueForCourse(vm, title) {
            if (!vm || typeof vm !== "object") return null;
            const keys = Object.keys(vm);
            for (const key of keys) {
                if (key.startsWith("_") || key === "constructor") continue;
                try {
                    const val = vm[key];
                    if (!val || typeof val !== "object") continue;
                    if (Array.isArray(val)) {
                        for (const item of val) {
                            if (item && typeof item === "object") {
                                const matchTitle = item.title || item.courseName || item.name;
                                if (matchTitle === title) {
                                    if (item.id || item.courseId || item.dsUnitId) {
                                        return {
                                            id: item.id || item.courseId || item.dsUnitId,
                                            link: item.link || item.url || item.href || item.studyLink
                                        };
                                    }
                                }
                            }
                        }
                    }
                    if (val.id && /^[a-f0-9-]{8}/i.test(String(val.id))) {
                        const matchTitle = val.title || val.courseName || val.name;
                        if (matchTitle === title) {
                            return {
                                id: val.id,
                                link: val.link || val.url || val.href
                            };
                        }
                    }
                    if ((val.link || val.url || val.href) && String(val.link || val.url || val.href).includes("/study/course/detail/")) {
                        const matchTitle = val.title || val.courseName || val.name;
                        if (matchTitle === title) {
                            const link = val.link || val.url || val.href;
                            const idMatch = link.match(/detail\/(?:[^/]*@@)?([a-f0-9-]{36})/i);
                            return {
                                id: idMatch ? idMatch[1] : null,
                                link: link
                            };
                        }
                    }
                } catch (e) {}
            }
            if (vm.$data) {
                const data = vm.$data;
                const listKeys = [ "list", "courses", "courseList", "items", "data", "tableData" ];
                for (const listKey of listKeys) {
                    const list = data[listKey];
                    if (Array.isArray(list)) {
                        for (const item of list) {
                            if (item && typeof item === "object") {
                                const matchTitle = item.title || item.courseName || item.name;
                                if (matchTitle === title) {
                                    if (item.id || item.courseId) {
                                        return {
                                            id: item.id || item.courseId,
                                            link: item.link || item.url || item.href
                                        };
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return null;
        },
        _getTotalPagesFromText() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const paginationBox = document.querySelector(config.PAGINATION_BOX_SELECTOR);
            if (paginationBox) {
                const pageText = paginationBox.textContent || "";
                const totalMatch = pageText.match(/共\s*(\d+)\s*页/);
                if (totalMatch) {
                    const total = parseInt(totalMatch[1], 10);
                    console.log(`[CbeadScanner] 从文本匹配到总页数: ${total}`);
                    return total;
                }
                const slashMatch = pageText.match(/(\d+)\s*\/\s*(\d+)/);
                if (slashMatch) {
                    const total = parseInt(slashMatch[2], 10);
                    console.log(`[CbeadScanner] 从 slash 格式匹配到总页数: ${total}`);
                    return total;
                }
            }
            const lastPageBtn = document.querySelector(`${config.PAGINATION_SELECTOR} .zxy-pagination-item-last`);
            if (lastPageBtn) {
                const lastPage = lastPageBtn.getAttribute("data-page");
                if (lastPage) {
                    const total = parseInt(lastPage, 10);
                    console.log(`[CbeadScanner] 从尾页按钮匹配到总页数: ${total}`);
                    return total;
                }
            }
            console.warn(`[CbeadScanner] 无法从文本确定总页数`);
            return 0;
        },
        _getTotalPages() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const paginationBox = document.querySelector(config.PAGINATION_BOX_SELECTOR);
            if (paginationBox) {
                const pageText = paginationBox.textContent || "";
                const totalMatch = pageText.match(/共\s*(\d+)\s*页/);
                if (totalMatch) {
                    const total = parseInt(totalMatch[1], 10);
                    console.log(`[CbeadScanner] 从文本匹配到总页数: ${total}`);
                    return total;
                }
                const slashMatch = pageText.match(/(\d+)\s*\/\s*(\d+)/);
                if (slashMatch) {
                    const total = parseInt(slashMatch[2], 10);
                    console.log(`[CbeadScanner] 从 slash 格式匹配到总页数: ${total}`);
                    return total;
                }
            }
            const lastPageBtn = document.querySelector(`${config.PAGINATION_SELECTOR} .zxy-pagination-item-last`);
            if (lastPageBtn) {
                const lastPage = lastPageBtn.getAttribute("data-page");
                if (lastPage) {
                    const total = parseInt(lastPage, 10);
                    console.log(`[CbeadScanner] 从尾页按钮匹配到总页数: ${total}`);
                    return total;
                }
            }
            console.warn(`[CbeadScanner] 无法确定总页数,默认为 1`);
            return 1;
        },
        _isPageLoaded() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const container = document.querySelector(config.CONTAINER_SELECTOR);
            const items = container?.querySelectorAll(config.ITEM_SELECTOR);
            return items && items.length > 0;
        },
        async _waitForPageLoad(timeout) {
            const startTime = Date.now();
            while (Date.now() - startTime < timeout) {
                if (this._isPageLoaded()) {
                    return true;
                }
                await new Promise(resolve => setTimeout(resolve, 500));
            }
            return false;
        },
        _isLastPage() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const nextBtn = document.querySelector(config.NEXT_BTN_SELECTOR);
            if (nextBtn) {
                const parentLi = nextBtn.closest("li");
                if (parentLi && parentLi.classList.contains("zxy-pagination-disabled")) {
                    console.log(`[CbeadScanner] 检测到父元素 li 包含禁用类(最后一页)`);
                    return true;
                }
                if (nextBtn.classList.contains("is-disabled") || nextBtn.classList.contains("disabled") || nextBtn.classList.contains(config.DISABLED_PAGINATION_CLASS)) {
                    console.log(`[CbeadScanner] 检测到下一页按钮包含禁用类(最后一页)`);
                    return true;
                }
                if (nextBtn.hasAttribute("disabled") || nextBtn.getAttribute("aria-disabled") === "true") {
                    console.log(`[CbeadScanner] 检测到下一页按钮已禁用(最后一页)`);
                    return true;
                }
                const style = nextBtn.getAttribute("style") || "";
                if (style.includes("display: none") || style.includes("visibility: hidden")) {
                    console.log(`[CbeadScanner] 检测到下一页按钮隐藏(最后一页)`);
                    return true;
                }
            }
            const disabledNext = document.querySelector(".zxy-pagination-next.is-disabled, .zxy-pagination-next.disabled, .zxy-pagination-next.zxy-pagination-disabled");
            if (disabledNext) {
                console.log(`[CbeadScanner] 检测到禁用的下一页按钮(最后一页)`);
                return true;
            }
            const nextLi = document.querySelector(".zxy-pagination-next")?.closest("li");
            if (nextLi && nextLi.classList.contains("zxy-pagination-disabled")) {
                console.log(`[CbeadScanner] 检测到下一页父元素 li.zxy-pagination-disabled(最后一页)`);
                return true;
            }
            const disabledItems = document.querySelectorAll(`${config.PAGINATION_SELECTOR} .is-disabled, ${config.PAGINATION_SELECTOR} .disabled`);
            for (const item of disabledItems) {
                if (item.classList.contains("zxy-pagination-next") || item.querySelector(".zxy-pagination-next")) {
                    console.log(`[CbeadScanner] 检测到禁用的下一页分页项(最后一页)`);
                    return true;
                }
            }
            const currentPage = this._getCurrentPage();
            const totalPages = this._getTotalPagesFromText();
            console.log(`[CbeadScanner] 当前页: ${currentPage}, 总页数: ${totalPages}`);
            if (currentPage >= totalPages && totalPages > 0) {
                console.log(`[CbeadScanner] 当前页码 >= 总页数(最后一页)`);
                return true;
            }
            return false;
        },
        async _clickNextPage() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            if (this._isLastPage()) {
                console.log(`[CbeadScanner] 已到达最后一页`);
                return false;
            }
            const paginationContainer = document.querySelector(config.PAGINATION_BOX_SELECTOR);
            if (!paginationContainer) {
                console.warn(`[CbeadScanner] 未找到分页容器: ${config.PAGINATION_BOX_SELECTOR}`);
                return false;
            }
            const nextBtn = paginationContainer.querySelector(".zxy-pagination-next");
            if (!nextBtn) {
                console.warn(`[CbeadScanner] 未找到下一页按钮`);
                return false;
            }
            const currentUrl = window.location.href;
            console.log(`[CbeadScanner] 点击前 URL: ${currentUrl}`);
            const parentLi = nextBtn.closest("li");
            if (parentLi) {
                console.log(`[CbeadScanner] 找到下一页按钮,父元素类名: ${parentLi.className}`);
            }
            if (parentLi && parentLi.classList.contains("zxy-pagination-disabled")) {
                console.log(`[CbeadScanner] 下一页按钮父元素 li 已禁用(最后一页)`);
                return false;
            }
            if (nextBtn.classList.contains("is-disabled") || nextBtn.classList.contains("disabled") || nextBtn.classList.contains(config.DISABLED_PAGINATION_CLASS) || nextBtn.hasAttribute("disabled") || nextBtn.getAttribute("aria-disabled") === "true") {
                console.log(`[CbeadScanner] 下一页按钮已禁用(最后一页)`);
                return false;
            }
            const currentPage = this._getCurrentPage();
            console.log(`[CbeadScanner] 正在从第 ${currentPage} 页点击下一页...`);
            nextBtn.click();
            console.log(`[CbeadScanner] ✅ 已点击下一页按钮`);
            await new Promise(resolve => setTimeout(resolve, config.PAGINATION_DELAY));
            console.log(`[CbeadScanner] 点击后 URL: ${window.location.href}`);
            if (window.location.pathname === "/" && !window.location.hash) {
                console.error(`[CbeadScanner] ⚠️ 检测到异常跳转到根路径!`);
                return false;
            }
            let newPage = this._getCurrentPage();
            let attempts = 0;
            const maxAttempts = 5;
            while (newPage === currentPage && attempts < maxAttempts) {
                attempts++;
                console.log(`[CbeadScanner] 页面未变化(第${attempts}次尝试),继续等待...`);
                if (this._isLastPage()) {
                    console.log(`[CbeadScanner] 检测到已到达最后一页`);
                    return false;
                }
                await new Promise(resolve => setTimeout(resolve, 500));
                newPage = this._getCurrentPage();
            }
            console.log(`[CbeadScanner] 页面变化: ${currentPage} -> ${newPage}`);
            if (newPage === currentPage) {
                console.warn(`[CbeadScanner] 页面未变化,可能已到最后一页或加载失败`);
                if (this._isLastPage()) {
                    return false;
                }
                console.warn(`[CbeadScanner] 页面加载异常,停止翻页`);
                return false;
            }
            const totalPages = this._getTotalPagesFromText();
            if (totalPages > 0 && newPage > totalPages) {
                console.warn(`[CbeadScanner] 页码异常: 新页码 ${newPage} 超过总页数 ${totalPages}`);
                return false;
            }
            return true;
        },
        _getCurrentPage() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const activeItem = document.querySelector(config.ACTIVE_PAGE_SELECTOR);
            if (activeItem) {
                const pageNum = parseInt(activeItem.textContent?.trim(), 10);
                if (!isNaN(pageNum)) {
                    return pageNum;
                }
            }
            const isActiveItem = document.querySelector(`${config.PAGINATION_SELECTOR} .is-active`);
            if (isActiveItem) {
                const pageNum = parseInt(isActiveItem.textContent?.trim(), 10);
                if (!isNaN(pageNum)) {
                    return pageNum;
                }
            }
            return 1;
        },
        async scanAllCoursesWithPagination() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const allCourses = [];
            let currentPage = 1;
            console.log(`[CbeadScanner] 开始翻页扫描课程...`);
            while (true) {
                const pageCourses = await this.scanCoursesFromBranchListPage();
                console.log(`[CbeadScanner] 第 ${currentPage} 页: 扫描到 ${pageCourses.length} 门课程`);
                allCourses.push(...pageCourses);
                const hasNext = await this._clickNextPage();
                if (!hasNext) {
                    console.log(`[CbeadScanner] 已到达最后一页,停止扫描`);
                    break;
                }
                currentPage++;
                const loaded = await this._waitForPageLoad(config.PAGE_LOAD_TIMEOUT);
                if (!loaded) {
                    console.warn(`[CbeadScanner] 页面 ${currentPage} 加载超时,停止扫描`);
                    break;
                }
            }
            console.log(`[CbeadScanner] 翻页扫描完成,共 ${allCourses.length} 门课程`);
            console.log(`[CbeadScanner] 📤 返回 ${allCourses.length} 门课程`);
            return allCourses;
        },
        _getTagList() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const tagContainer = document.querySelector(config.TAG_CONTAINER_SELECTOR);
            if (!tagContainer) {
                console.warn(`[CbeadScanner] 未找到标签容器: ${config.TAG_CONTAINER_SELECTOR}`);
                return [];
            }
            const tagBtns = tagContainer.querySelectorAll(config.TAG_BTN_SELECTOR);
            return Array.from(tagBtns);
        },
        async clickTagFilter(tagBtn) {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            if (!tagBtn) {
                return false;
            }
            if (tagBtn.classList.contains("is-active") || tagBtn.classList.contains("active")) {
                console.log(`[CbeadScanner] 标签已激活,跳过`);
                return false;
            }
            const tagText = tagBtn.textContent?.trim() || "未知标签";
            console.log(`[CbeadScanner] 点击标签: ${tagText}`);
            tagBtn.click();
            await new Promise(resolve => setTimeout(resolve, config.TAG_SWITCH_DELAY));
            return true;
        },
        async scanAllCoursesWithTagFilter(skipAllTag = true) {
            const allCourses = [];
            const seenLinks = new Set;
            console.log(`[CbeadScanner] 开始扫描所有标签下的课程...`);
            const tagList = this._getTagList();
            if (tagList.length === 0) {
                console.warn(`[CbeadScanner] 未找到标签,尝试普通扫描`);
                return this.scanAllCoursesWithPagination();
            }
            for (let i = 0; i < tagList.length; i++) {
                const tagBtn = tagList[i];
                const tagText = tagBtn.textContent?.trim() || `标签${i + 1}`;
                if (skipAllTag && (tagText === "全部" || tagText === "All" || tagText === "全部标签")) {
                    console.log(`[CbeadScanner] 跳过"全部"标签`);
                    continue;
                }
                console.log(`[CbeadScanner] 处理标签: ${tagText}`);
                if (i > 0 || !skipAllTag) {
                    const clicked = await this.clickTagFilter(tagBtn);
                    if (!clicked) {
                        console.log(`[CbeadScanner] 标签 ${tagText} 无需切换或切换失败`);
                    }
                }
                const tagCourses = await this.scanAllCoursesWithPagination();
                for (const course of tagCourses) {
                    if (!seenLinks.has(course.link)) {
                        seenLinks.add(course.link);
                        allCourses.push(course);
                    }
                }
                console.log(`[CbeadScanner] 标签 ${tagText}: ${tagCourses.length} 门课程(累计 ${allCourses.length} 门)`);
            }
            console.log(`[CbeadScanner] 标签扫描完成,共 ${allCourses.length} 门课程(去重后)`);
            return allCourses;
        },
        async scanCoursesFromBranchList(options = {}) {
            const {useTagFilter: useTagFilter = false, skipAllTag: skipAllTag = true} = options;
            console.log(`[CbeadScanner] 开始扫描 branch-list-v 页面`);
            console.log(`[CbeadScanner] 选项: useTagFilter=${useTagFilter}, skipAllTag=${skipAllTag}`);
            console.log(`[CbeadScanner] 当前页面URL: ${window.location.href}`);
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            let container = document.querySelector(config.CONTAINER_SELECTOR);
            let attempts = 0;
            const maxAttempts = 20;
            while (!container && attempts < maxAttempts) {
                await new Promise(resolve => setTimeout(resolve, 500));
                container = document.querySelector(config.CONTAINER_SELECTOR);
                attempts++;
            }
            if (attempts > 0) {
                console.log(`[CbeadScanner] 等待页面加载完成,尝试 ${attempts} 次`);
            }
            if (useTagFilter) {
                console.log(`[CbeadScanner] 使用标签筛选模式...`);
                return await this.scanAllCoursesWithTagFilter(skipAllTag);
            } else {
                console.log(`[CbeadScanner] 使用普通翻页模式...`);
                return await this.scanAllCoursesWithPagination();
            }
        },
        async scanCoursesFromColumnPage() {
            console.log(`[CbeadScanner] 开始扫描专题班课程...`);
            const pageStyle = this._detectColumnPageStyle();
            console.log(`[CbeadScanner] 检测到页面样式: ${pageStyle}`);
            if (pageStyle === "activity") {
                return await this._scanFromActivityPageStyle();
            } else if (pageStyle === "layout") {
                return await this._scanFromLayoutStyle();
            } else {
                console.warn(`[CbeadScanner] 未识别的页面样式,尝试所有扫描方法`);
                let courses = await this._scanFromActivityPageStyle();
                if (courses.length > 0) return courses;
                courses = await this._scanFromLayoutStyle();
                return courses;
            }
        },
        _detectColumnPageStyle() {
            const config = CBEAD_CONSTANTS.COLUMN_PAGE;
            const activityContainer = document.querySelector(config.ACTIVITY_STYLE.CONTAINER_SELECTOR);
            if (activityContainer) {
                if (activityContainer.querySelector("ul.list")) {
                    console.log(`[CbeadScanner] 检测到活动清单样式 (.activity-page) - 通过 ul.list`);
                    return "activity";
                }
                const courseItems = activityContainer.querySelectorAll("li.clearfix");
                if (courseItems.length > 0) {
                    console.log(`[CbeadScanner] 检测到活动清单样式 (.activity-page) - 通过课程项`);
                    return "activity";
                }
                const allItems = activityContainer.querySelectorAll('[class*="activity-stage"]');
                if (allItems.length > 0) {
                    console.log(`[CbeadScanner] 检测到活动清单样式 (.activity-page) - 通过 stage 元素`);
                    return "activity";
                }
                console.log(`[CbeadScanner] 检测到活动清单样式 (.activity-page) - 容器存在`);
                return "activity";
            }
            const layoutContainer = document.querySelector(config.LAYOUT_STYLE.CONTAINER_SELECTOR);
            if (layoutContainer) {
                const listElements = layoutContainer.querySelector(config.LAYOUT_STYLE.LIST_SELECTOR);
                if (listElements || layoutContainer.querySelector(config.LAYOUT_STYLE.ITEM_SELECTOR)) {
                    console.log(`[CbeadScanner] 检测到layout样式 (.class-layout)`);
                    return "layout";
                }
            }
            console.log(`[CbeadScanner] 无法确定页面样式`);
            return "unknown";
        },
        async _scanFromActivityPageStyle() {
            const config = CBEAD_CONSTANTS.COLUMN_PAGE.ACTIVITY_STYLE;
            let container = document.querySelector(config.CONTAINER_SELECTOR);
            let attempts = 0;
            const maxAttempts = 20;
            while (!container && attempts < maxAttempts) {
                await new Promise(resolve => setTimeout(resolve, 500));
                container = document.querySelector(config.CONTAINER_SELECTOR);
                attempts++;
            }
            if (attempts > 0) {
                console.log(`[CbeadScanner] 等待页面加载完成,尝试 ${attempts} 次`);
            }
            const courses = [];
            if (!container) {
                console.warn(`[CbeadScanner] 未找到专题班课程列表容器: ${config.CONTAINER_SELECTOR}`);
                return courses;
            }
            const allLists = container.querySelectorAll(".activity-stage ul.list");
            console.log(`[CbeadScanner] 专题班页面找到 ${allLists.length} 个课程列表模块`);
            allLists.forEach((list, moduleIndex) => {
                const moduleTitleEl = list.parentElement?.querySelector(".activity-stage-name");
                const moduleTitle = moduleTitleEl?.textContent?.trim() || `模块${moduleIndex + 1}`;
                const courseItems = list.querySelectorAll(config.ITEM_SELECTOR);
                console.log(`[CbeadScanner] ${moduleTitle}模块: 找到 ${courseItems.length} 个课程项`);
                courseItems.forEach((item, index) => {
                    try {
                        const titleEl = item.querySelector(config.TITLE_SELECTOR);
                        if (!titleEl) {
                            console.warn(`[CbeadScanner] ${moduleTitle} 课程 ${index + 1}: 未找到标题元素`);
                            return;
                        }
                        const idMatch = titleEl.id?.match(/D75itemDetail1-([a-f0-9-]+)/);
                        if (!idMatch) {
                            const altIdMatch = item.id?.match(/([a-f0-9-]{36})/i);
                            if (!altIdMatch) {
                                console.warn(`[CbeadScanner] ${moduleTitle} 课程 ${index + 1}: 无法提取课程ID`);
                                return;
                            }
                            var courseId = altIdMatch[1];
                        } else {
                            courseId = idMatch[1];
                        }
                        const title = titleEl.textContent?.trim() || `课程${courseId.substring(0, 8)}`;
                        const progressBar = item.querySelector(config.PROGRESS_BAR_SELECTOR);
                        const style = progressBar?.getAttribute("style") || "";
                        const progressMatch = style.match(/width:\s*(\d+)%/);
                        const progress = progressMatch ? parseInt(progressMatch[1]) : 0;
                        const studyLink = `#/study/course/detail/${courseId}`;
                        let status = "not_started";
                        if (progress >= CBEAD_CONSTANTS.THRESHOLDS.COMPLETED_PROGRESS) {
                            status = "completed";
                        } else if (progress > 0) {
                            status = "in_progress";
                        }
                        courses.push({
                            id: courseId,
                            courseId: courseId,
                            dsUnitId: courseId,
                            title: title,
                            courseName: title,
                            link: studyLink,
                            progress: progress,
                            isCompleted: progress >= CBEAD_CONSTANTS.THRESHOLDS.COMPLETED_PROGRESS,
                            status: status,
                            element: item,
                            source: "cbead_column_activity_style",
                            module: moduleTitle
                        });
                        const statusTextDisplay = {
                            completed: "✅ 已完成",
                            in_progress: "📖 学习中",
                            not_started: "📝 未开始"
                        };
                        console.log(`[CbeadScanner] ${moduleTitle} 课程 ${index + 1}: ${title} - ${statusTextDisplay[status]} (${progress}%)`);
                    } catch (error) {
                        console.error(`[CbeadScanner] ${moduleTitle} 处理课程项失败 (索引 ${index}):`, error);
                    }
                });
            });
            console.log(`[CbeadScanner] 活动清单样式扫描完成,共 ${courses.length} 门课程`);
            return courses;
        },
        async _scanFromLayoutStyle() {
            const config = CBEAD_CONSTANTS.COLUMN_PAGE.LAYOUT_STYLE;
            let container = document.querySelector(config.CONTAINER_SELECTOR);
            let attempts = 0;
            const maxAttempts = 20;
            while (!container && attempts < maxAttempts) {
                await new Promise(resolve => setTimeout(resolve, 500));
                container = document.querySelector(config.CONTAINER_SELECTOR);
                attempts++;
            }
            if (attempts > 0) {
                console.log(`[CbeadScanner] 等待页面加载完成,尝试 ${attempts} 次`);
            }
            if (!container) {
                console.warn(`[CbeadScanner] 未找到layout样式课程列表容器: ${config.CONTAINER_SELECTOR}`);
                return [];
            }
            const tabContainer = document.querySelector(config.TAB_SWITCH.CONTAINER_SELECTOR);
            const hasTabs = tabContainer !== null;
            if (hasTabs) {
                console.log(`[CbeadScanner] 检测到标签切换,将遍历所有标签`);
                return await this._scanFromLayoutStyleWithTabs(container, tabContainer);
            } else {
                console.log(`[CbeadScanner] 未检测到标签切换,扫描单个列表`);
                return await this._scanFromLayoutStyleSingle(container);
            }
        },
        async _scanFromLayoutStyleWithTabs(container, tabContainer) {
            const config = CBEAD_CONSTANTS.COLUMN_PAGE.LAYOUT_STYLE;
            const allCourses = [];
            const seenCourseIds = new Set;
            const tabItems = tabContainer.querySelectorAll(config.TAB_SWITCH.TAB_ITEM_SELECTOR);
            console.log(`[CbeadScanner] 找到 ${tabItems.length} 个标签`);
            for (let tabIndex = 0; tabIndex < tabItems.length; tabIndex++) {
                const tab = tabItems[tabIndex];
                const tabName = tab.textContent?.trim() || `标签${tabIndex + 1}`;
                const isActive = tab.classList.contains(config.TAB_SWITCH.ACTIVE_CLASS);
                if (!isActive) {
                    console.log(`[CbeadScanner] 切换到标签: ${tabName}`);
                    tab.click();
                    await new Promise(resolve => setTimeout(resolve, config.TAB_SWITCH.SWITCH_DELAY));
                } else {
                    console.log(`[CbeadScanner] 当前已在标签: ${tabName}`);
                }
                const courseList = container.querySelector(config.LIST_SELECTOR);
                if (!courseList) {
                    console.warn(`[CbeadScanner] 标签 "${tabName}" 未找到课程列表`);
                    continue;
                }
                const courseItems = courseList.querySelectorAll(config.ITEM_SELECTOR);
                console.log(`[CbeadScanner] 标签 "${tabName}": 找到 ${courseItems.length} 个课程项`);
                for (let itemIndex = 0; itemIndex < courseItems.length; itemIndex++) {
                    const item = courseItems[itemIndex];
                    try {
                        const courseInfo = this._extractCourseFromLayoutItem(item, config, itemIndex, tabName);
                        if (courseInfo && courseInfo.id && !seenCourseIds.has(courseInfo.id)) {
                            seenCourseIds.add(courseInfo.id);
                            allCourses.push(courseInfo);
                        } else if (courseInfo && seenCourseIds.has(courseInfo.id)) {
                            console.log(`[CbeadScanner] 标签 "${tabName}" 课程 ${itemIndex + 1}: ${courseInfo.title} - 已存在,跳过`);
                        }
                    } catch (error) {
                        console.error(`[CbeadScanner] 标签 "${tabName}" 处理课程项失败 (索引 ${itemIndex}):`, error);
                    }
                }
            }
            console.log(`[CbeadScanner] layout样式(带标签)扫描完成,共 ${allCourses.length} 门课程(去重后)`);
            return allCourses;
        },
        async _scanFromLayoutStyleSingle(container) {
            const config = CBEAD_CONSTANTS.COLUMN_PAGE.LAYOUT_STYLE;
            const courses = [];
            const courseList = container.querySelector(config.LIST_SELECTOR);
            if (!courseList) {
                console.warn(`[CbeadScanner] 未找到课程列表: ${config.LIST_SELECTOR}`);
                return courses;
            }
            const courseItems = courseList.querySelectorAll(config.ITEM_SELECTOR);
            console.log(`[CbeadScanner] layout样式找到 ${courseItems.length} 个课程项`);
            courseItems.forEach((item, index) => {
                try {
                    const courseInfo = this._extractCourseFromLayoutItem(item, config, index);
                    if (courseInfo) {
                        courses.push(courseInfo);
                    }
                } catch (error) {
                    console.error(`[CbeadScanner] layout样式 处理课程项失败 (索引 ${index}):`, error);
                }
            });
            console.log(`[CbeadScanner] layout样式扫描完成,共 ${courses.length} 门课程`);
            return courses;
        },
        _extractCourseFromLayoutItem(item, config, index, tabName = "") {
            const titleEl = item.querySelector(config.TITLE_SELECTOR);
            if (!titleEl) {
                console.warn(`[CbeadScanner] ${tabName ? `标签"${tabName}" ` : ""}课程 ${index + 1}: 未找到标题元素`);
                return null;
            }
            let courseId = null;
            const idMatch = titleEl.id?.match(/D74itemDetail-([a-f0-9-]+)/i);
            if (idMatch) {
                courseId = idMatch[1];
            } else {
                courseId = item.getAttribute("data-activityid");
                if (!courseId) {
                    const altIdMatch = titleEl.id?.match(/([a-f0-9-]{36})/i);
                    if (altIdMatch) {
                        courseId = altIdMatch[1];
                    } else {
                        console.warn(`[CbeadScanner] ${tabName ? `标签"${tabName}" ` : ""}课程 ${index + 1}: 无法提取课程ID`);
                        return null;
                    }
                }
            }
            const title = titleEl.textContent?.trim() || `课程${courseId.substring(0, 8)}`;
            const statusEl = item.querySelector(config.STATUS_SELECTOR);
            const statusText = statusEl?.textContent?.trim() || "";
            let progress = 0;
            let status = "not_started";
            if (statusText === config.STATUS_TEXT.COMPLETED) {
                status = "completed";
                progress = 100;
            } else if (statusText === config.STATUS_TEXT.IN_PROGRESS) {
                status = "in_progress";
                progress = 50;
            } else if (statusText === config.STATUS_TEXT.UNFINISHED) {
                status = "not_started";
                progress = 0;
            } else {
                status = "not_started";
                progress = 0;
            }
            const studyLink = `#/study/course/detail/${courseId}`;
            const statusTextDisplay = {
                completed: "✅ 已完成",
                in_progress: "📖 学习中",
                not_started: "📝 未开始"
            };
            const tabPrefix = tabName ? `[${tabName}] ` : "";
            console.log(`[CbeadScanner] ${tabPrefix}课程 ${index + 1}: ${title} - ${statusTextDisplay[status]} (${progress}%) [状态文本: "${statusText}"]`);
            return {
                id: courseId,
                courseId: courseId,
                dsUnitId: courseId,
                title: title,
                courseName: title,
                link: studyLink,
                progress: progress,
                isCompleted: progress >= CBEAD_CONSTANTS.THRESHOLDS.COMPLETED_PROGRESS,
                status: status,
                element: item,
                source: "cbead_column_layout_style",
                statusText: statusText,
                tabName: tabName || null
            };
        },
        detectPageType() {
            const hash = window.location.hash || "";
            if (hash.includes(CBEAD_CONSTANTS.PATH_PATTERNS.BRANCH_LIST)) {
                return {
                    pageType: "branch-list",
                    pageName: "在线自学课程列表",
                    scanMethod: () => this.scanCoursesFromBranchListPage()
                };
            }
            if (hash.includes(CBEAD_CONSTANTS.PATH_PATTERNS.COLUMN)) {
                return {
                    pageType: "column",
                    pageName: "专题班详情",
                    scanMethod: () => this.scanCoursesFromColumnPage()
                };
            }
            const branchContainer = document.querySelector(CBEAD_CONSTANTS.BRANCH_LIST.CONTAINER_SELECTOR);
            if (branchContainer) {
                return {
                    pageType: "branch-list",
                    pageName: "在线自学课程列表",
                    scanMethod: () => this.scanCoursesFromBranchListPage()
                };
            }
            const columnContainerActivity = document.querySelector(CBEAD_CONSTANTS.COLUMN_PAGE.ACTIVITY_STYLE.CONTAINER_SELECTOR);
            const columnContainerLayout = document.querySelector(CBEAD_CONSTANTS.COLUMN_PAGE.LAYOUT_STYLE.CONTAINER_SELECTOR);
            if (columnContainerActivity || columnContainerLayout) {
                return {
                    pageType: "column",
                    pageName: "专题班详情",
                    scanMethod: () => this.scanCoursesFromColumnPage()
                };
            }
            return {
                pageType: "unknown",
                pageName: "未知页面",
                scanMethod: null
            };
        },
        async _extractFromVueDeepScan() {
            const courses = [];
            try {
                const vueInstances = [];
                const allElements = document.querySelectorAll("*");
                for (const el of allElements) {
                    if (el.__vue__) {
                        vueInstances.push(el.__vue__);
                    }
                }
                console.log(`[CbeadScanner] 找到 ${vueInstances.length} 个Vue实例进行深度扫描`);
                for (const instance of vueInstances) {
                    const instanceCourses = this._deepScanVueInstance(instance);
                    for (const course of instanceCourses) {
                        if (!courses.find(c => c.id === course.id)) {
                            courses.push(course);
                        }
                    }
                }
            } catch (error) {
                console.debug("[CbeadScanner] Vue深度扫描失败:", error.message);
            }
            return courses;
        },
        _deepScanVueInstance(obj, visited = new Set, depth = 0) {
            const courses = [];
            const maxDepth = 15;
            const maxItems = 5e3;
            if (depth > maxDepth) return courses;
            if (visited.size > maxItems) return courses;
            if (!obj || typeof obj !== "object") return courses;
            visited.add(obj);
            try {
                if (Array.isArray(obj)) {
                    for (const item of obj) {
                        if (item && typeof item === "object") {
                            const courseInfo = this._tryBuildCourseInfo(item);
                            if (courseInfo) {
                                courses.push(courseInfo);
                            } else {
                                courses.push(...this._deepScanVueInstance(item, visited, depth + 1));
                            }
                        }
                    }
                } else {
                    const courseInfo = this._tryBuildCourseInfo(obj);
                    if (courseInfo) {
                        courses.push(courseInfo);
                    }
                    for (const key of Object.keys(obj)) {
                        if (key.startsWith("_") || key === "constructor") continue;
                        try {
                            const value = obj[key];
                            if (value && typeof value === "object" && !visited.has(value)) {
                                courses.push(...this._deepScanVueInstance(value, visited, depth + 1));
                            }
                        } catch (e) {}
                    }
                }
            } catch (error) {}
            return courses;
        },
        _tryBuildCourseInfo(data) {
            if (!data || typeof data !== "object") return null;
            const id = data.id || data.courseId || data.dsUnitId || data.courseCode;
            const title = data.title || data.courseName || data.name || data.courseTitle;
            if (!id || !title) return null;
            if (!/^[a-f0-9]{8}-?[a-f0-9]{4}/i.test(String(id))) {
                return null;
            }
            const lowerTitle = String(title).toLowerCase();
            const invalidKeywords = [ "分院", "主页", "专题班", "在线自学", "专栏", "个人中心", "AI教练", "最近活动", "往期回顾", "课程目录体系", "其他课程", "分享到问道", "默认配置", "配置", "设置", "导航", "菜单", "页脚", "皮肤", "标签", "学习任务", "数据归档", "密码安全", "第三方平台", "最近学习", "备案信息", "登录页", "协议", "logo", "勋章", "纷享", "打赏", "限流", "防刷", "飘窗", "学币", "分院统计", "语言配置", "播放器", "数据导出", "账户登录", "身份验证", "证书获取", "人脸", "重操作", "防录屏", "扫码下载", "扫码关注", "问吧", "知识付费", "APP状态栏", "移动端", "管理员", "默认首页", "详情页", "个人信息", "网页置灰", "分享", "重操作显示", "课程目录体系", "其他课程", "系统配置", "系统设置", "微信", "QQ空间", "新浪微搏", "微博", "安徽", "北京", "上海", "广东", "深圳", "浙江", "江苏", "政治理论", "党性教育", "时政热点", "宏观经济", "产业发展", "管理理论", "实践", "国企改革", "国企党建", "转型升级", "人文素养", "安全生产", "工会工作", "合规管理", "创新管理", "二十大", "三中全会", "现代企业制度", "理想信念", "创新思维", "战略管理", "八项规定", "创新转型", "ESG", "领导力", "科改", "技术发展", "调查研究", "行业发展", "主题教育", "职业素养", "人才培养", "人工智能", "数字化转型", "碳达峰", "碳中和", "党的宗旨", "党建生产", "深度融合", "世界一流", "新质生产力", "资本运营", "财务管理", "企业文化", "廉政教育", "全面从严治党", "市场化", "经营机制", "金融形势", "品牌建设", "党史", "国史", "科技创新", "好干部", "国企改革趋势", "总体要求", "国有企业简史", "特色课程", "乡村振兴", "转型发展", "领导力", "形势政策", "政治经济", "并购重组", "传统文化", "审计", "应急管理", "纪检监察", "产业形势", "思想政治", "职场胜任", "宏观形势", "行业领域", "资本市场", "生态文明", "采购管理", "治国理政", "危机领导力", "绿色发展", "数智化", "国际化", "涉外", "风险管理", "业财融合", "新发展", "政治能力", "创新发展", "激励与绩效", "金融风险", "支部建设", "理论学习", "国资监管", "风控体系", "条例", "变革领导力", "报表分析", "团队领导力", "混合所有制", "新闻宣传", "国际化经营", "国际形势", "梯队建设", "演讲与沟通", "三项制度", "外交政策", "绿色金融", "媒介素养", "廉洁从业", "宏观政策", "形势教育", "公共关系", "物流与供应链", "战略人力", "基本理论", "产业创新", "企业管理", "党建", "改革发展", "个人修养", "党性修养", "政治能力" ];
            for (const keyword of invalidKeywords) {
                if (lowerTitle.includes(keyword.toLowerCase())) {
                    return null;
                }
            }
            const invalidPrefixes = [ "系统", "后台", "配置", "设置", "菜单" ];
            for (const prefix of invalidPrefixes) {
                if (lowerTitle.startsWith(prefix.toLowerCase())) {
                    return null;
                }
            }
            if (/^[a-f0-9]{32}$/i.test(lowerTitle) || /^[a-f0-9]{8}-[a-f0-9]{4}/i.test(lowerTitle)) {
                return null;
            }
            const titleLength = String(title).length;
            if (titleLength < 4 || titleLength > 50) {
                return null;
            }
            return this._buildCourseInfoFromApiData(data);
        },
        async detectCourseStatusBatch(courseItems) {
            if (!Array.isArray(courseItems) || courseItems.length === 0) {
                console.warn("[CbeadScanner] 检测状态收到空课程列表");
                return [];
            }
            console.log(`[CbeadScanner] 开始批量检测课程状态,共 ${courseItems.length} 个课程项`);
            const hasDomElements = courseItems.some(item => item && typeof item.querySelector === "function");
            let statusResults;
            if (hasDomElements) {
                statusResults = CourseStatusDetector.detectBatch(courseItems);
            } else {
                console.log(`[CbeadScanner] 输入为课程对象数组,跳过状态检测`);
                statusResults = courseItems.map((item, index) => {
                    const status = item.status || "not_started";
                    const progress = item.progress || 0;
                    return {
                        index: index,
                        element: item.element || null,
                        status: status,
                        confidence: 100,
                        isCompleted: item.isCompleted || progress >= 100,
                        isInProgress: status === "in_progress",
                        isNotStarted: status === "not_started",
                        title: item.title || item.courseName || "未知课程",
                        courseId: item.id || item.courseId || null
                    };
                });
            }
            const coursesWithStatus = statusResults.map((result, index) => {
                const item = courseItems[index];
                const title = result.title || item && (item.title || item.courseName) || this._safeExtractTitle(item);
                const courseId = result.courseId || item && (item.id || item.courseId) || this._safeExtractId(item);
                let status = "not_started";
                let progress = 0;
                if (result.status === CourseStatusDetector.STATUS.COMPLETED) {
                    status = "completed";
                    progress = 100;
                } else if (result.status === CourseStatusDetector.STATUS.IN_PROGRESS) {
                    status = "in_progress";
                    progress = 50;
                }
                return {
                    id: courseId,
                    courseId: courseId,
                    dsUnitId: courseId,
                    title: title,
                    courseName: title,
                    link: item?.link || (courseId ? `#/study/course/detail/${courseId}` : null),
                    element: item?.element || item,
                    progress: progress,
                    isCompleted: result.isCompleted,
                    status: status,
                    statusConfidence: result.confidence,
                    statusSources: result.sources,
                    source: "cbead_status_detector"
                };
            });
            const stats = CourseStatusDetector.getStatistics(statusResults);
            console.log(`[CbeadScanner] 状态检测统计: 全部${stats.total}, 已完成${stats.completed}, 学习中${stats.inProgress}, 未开始${stats.notStarted}, 平均置信度${stats.avgConfidence}%`);
            return coursesWithStatus;
        },
        _safeExtractTitle(element) {
            if (!element || typeof element.querySelector !== "function") {
                return "未知课程";
            }
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const titleEl = element.querySelector(config.TITLE_SELECTOR);
            return titleEl?.textContent?.trim() || "未知课程";
        },
        _safeExtractId(element) {
            if (!element || typeof element.querySelector !== "function") {
                return null;
            }
            const linkEl = element.querySelector('a[href*="/study/course/detail/"]');
            if (linkEl) {
                const href = linkEl.getAttribute("href");
                const match = href.match(/detail\/([^&\/]+)/);
                if (match) return match[1];
            }
            return element.getAttribute("data-id") || null;
        },
        _extractIdFromElement(element) {
            const linkEl = element.querySelector('a[href*="/study/course/detail/"]');
            if (linkEl) {
                const href = linkEl.getAttribute("href");
                const match = href.match(/detail\/([^&\/]+)/);
                if (match) return match[1];
            }
            return element.getAttribute("data-id") || null;
        },
        async getLearningQueueWithSmartFilter(courseItems) {
            console.log("[CbeadScanner] 使用智能过滤获取学习队列...");
            const courses = await this.detectCourseStatusBatch(courseItems);
            const toLearn = CourseStatusDetector.filterLearningNeeded(courses.map(c => ({
                status: c.status,
                confidence: c.statusConfidence,
                title: c.title
            })));
            const courseIdsToLearn = new Set(toLearn.map(t => t.title));
            const learningQueue = courses.filter(c => courseIdsToLearn.has(c.title));
            const stats = {
                total: courses.length,
                toLearn: learningQueue.length,
                skip: courses.length - learningQueue.length,
                byStatus: {
                    completed: courses.filter(c => c.status === "completed").length,
                    inProgress: courses.filter(c => c.status === "in_progress").length,
                    notStarted: courses.filter(c => c.status === "not_started").length
                }
            };
            console.log(`[CbeadScanner] 智能过滤结果: 共${stats.total}门, 待学习${stats.toLearn}门, 跳过${stats.skip}门`);
            return {
                courses: learningQueue,
                statistics: stats
            };
        },
        async validateAndInitializePage() {
            console.log("[CbeadScanner] 验证并初始化页面...");
            const pageInfo = await BranchListValidator.validateAndGetInfo();
            if (!pageInfo.isValid) {
                console.error("[CbeadScanner] 页面验证失败:", pageInfo.error);
                return {
                    success: false,
                    pageInfo: pageInfo
                };
            }
            console.log("[CbeadScanner] 页面验证成功:", {
                "页码": pageInfo.pageNumber,
                "总页数": pageInfo.totalPages,
                "课程数": pageInfo.courseCount
            });
            return {
                success: true,
                pageInfo: pageInfo
            };
        }
    };
    const learningState = {
        failed: false,
        failureReason: null,
        markFailed(reason) {
            this.failed = true;
            this.failureReason = reason;
            console.error(`[CbeadProgressManager] 🚨 课程已标记为失败: ${reason}`);
        },
        reset() {
            this.failed = false;
            this.failureReason = null;
        },
        isFailed() {
            return this.failed;
        },
        getFailureReason() {
            return this.failureReason;
        }
    };
    const CbeadProgressManager = {
        getLearningState() {
            return learningState;
        },
        clearLearningProgress() {
            try {
                localStorage.removeItem(CBEAD_CONSTANTS.STORAGE_KEYS.LEARNING_PROGRESS);
                console.log("[CbeadProgressManager] 学习进度已清除");
            } catch (error) {
                console.error("[CbeadProgressManager] 清除学习进度失败:", error);
            }
        },
        saveLearningQueue(learningList, totalCourses, pageUrl) {
            try {
                const data = {
                    learningList: learningList.map(c => ({
                        id: c.id,
                        title: c.title,
                        link: c.link,
                        progress: c.progress,
                        status: c.status
                    })),
                    totalCourses: totalCourses,
                    currentIndex: 0,
                    pageUrl: pageUrl,
                    timestamp: Date.now()
                };
                localStorage.setItem(CBEAD_CONSTANTS.STORAGE_KEYS.LEARNING_PROGRESS, JSON.stringify(data));
                console.log(`[CbeadProgressManager] 学习队列已保存: ${learningList.length} 门课程`);
            } catch (error) {
                console.error("[CbeadProgressManager] 保存学习队列失败:", error);
            }
        },
        loadLearningQueue() {
            try {
                const data = localStorage.getItem(CBEAD_CONSTANTS.STORAGE_KEYS.LEARNING_PROGRESS);
                if (!data) {
                    console.log("[CbeadProgressManager] 未找到学习队列");
                    return null;
                }
                const parsed = JSON.parse(data);
                const QUEUE_TIMEOUT = 24 * 60 * 60 * 1e3;
                if (Date.now() - parsed.timestamp > QUEUE_TIMEOUT) {
                    console.log("[CbeadProgressManager] 学习队列已超时,清除");
                    this.clearLearningProgress();
                    return null;
                }
                console.log(`[CbeadProgressManager] 学习队列已加载: 当前索引 ${parsed.currentIndex}/${parsed.learningList.length}`);
                return parsed;
            } catch (error) {
                console.error("[CbeadProgressManager] 加载学习队列失败:", error);
                return null;
            }
        },
        updateCurrentIndex(newIndex) {
            try {
                const data = this.loadLearningQueue();
                if (data) {
                    data.currentIndex = newIndex;
                    data.timestamp = Date.now();
                    localStorage.setItem(CBEAD_CONSTANTS.STORAGE_KEYS.LEARNING_PROGRESS, JSON.stringify(data));
                    console.log(`[CbeadProgressManager] 当前索引已更新: ${newIndex}`);
                }
            } catch (error) {
                console.error("[CbeadProgressManager] 更新索引失败:", error);
            }
        },
        hasValidQueue(currentUrl) {
            const data = this.loadLearningQueue();
            if (!data) return false;
            if (!data.learningList || !Array.isArray(data.learningList)) {
                console.warn("[CbeadProgressManager] 队列数据结构无效: 缺少 learningList");
                return false;
            }
            if (typeof data.currentIndex !== "number") {
                console.warn("[CbeadProgressManager] 队列数据结构无效: 缺少 currentIndex");
                return false;
            }
            if (data.learningList.length === 0) {
                console.warn("[CbeadProgressManager] 学习队列为空");
                return false;
            }
            if (data.currentIndex < 0 || data.currentIndex >= data.learningList.length) {
                console.warn(`[CbeadProgressManager] currentIndex ${data.currentIndex} 超出范围 [0, ${data.learningList.length})`);
                return false;
            }
            if (currentUrl && data.pageUrl) {
                try {
                    const currentUrlObj = new URL(currentUrl, "https://dummy.com");
                    const savedUrlObj = new URL(data.pageUrl, "https://dummy.com");
                    if (currentUrlObj.origin !== savedUrlObj.origin) {
                        console.warn("[CbeadProgressManager] URL 域名不匹配");
                        return false;
                    }
                    const currentPath = currentUrlObj.pathname || "";
                    const savedPath = savedUrlObj.pathname || "";
                    if (currentPath !== savedPath) {
                        if (!savedPath.startsWith(currentPath) && !currentPath.startsWith(savedPath)) {
                            console.warn(`[CbeadProgressManager] URL 路径不匹配: ${currentPath} vs ${savedPath}`);
                            return false;
                        }
                    }
                } catch (e) {
                    console.warn("[CbeadProgressManager] URL 解析失败,跳过 URL 匹配检查");
                }
            }
            console.log("[CbeadProgressManager] 学习队列有效");
            return true;
        },
        async returnToList(returnUrl) {
            console.log("[CbeadProgressManager] 准备返回列表页...");
            this.clearLearningProgress();
            await new Promise(resolve => setTimeout(resolve, CBEAD_CONSTANTS.TIMING.NEXT_COURSE_DELAY));
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `🔄 返回列表页,扫描后自动继续...`,
                type: "info"
            });
            if (returnUrl && !returnUrl.includes("study/course/detail")) {
                console.log(`[CbeadProgressManager] 🚀 返回列表页: ${returnUrl}`);
                window.location.href = returnUrl;
            } else {
                console.warn("[CbeadProgressManager] ⚠️ 没有有效的返回 URL,尝试使用浏览器后退");
                window.history.back();
            }
        },
        publishCompletionStats(totalCourses) {
            EventBus.publish(CONSTANTS.EVENTS.STATISTICS_UPDATE, {
                total: totalCourses,
                completed: totalCourses,
                learned: totalCourses,
                failed: 0,
                skipped: 0
            });
            EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, "学习完成");
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `🎉 专题班全部课程学习完成!共 ${totalCourses} 门课程`,
                type: "success"
            });
        },
        normalizeCourseId(rawId) {
            if (!rawId) return null;
            const uuidPattern = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
            if (uuidPattern.test(rawId)) {
                return rawId;
            }
            if (/^\d+$/.test(rawId)) {
                return rawId;
            }
            const match = rawId.match(/[a-f0-9-]{36}/);
            return match ? match[0] : rawId;
        },
        extractPlayerParams() {
            const hash = window.location.hash;
            const newMatch = hash.match(/detail\/(?:[^/]*@@)?([a-f0-9-]{36})/i);
            if (newMatch) {
                console.log(`[CbeadProgressManager] ✅ 使用新 URL 格式提取参数`);
                return {
                    uuid: newMatch[1],
                    courseId: newMatch[1],
                    coursewareId: newMatch[1]
                };
            }
            const oldMatch = hash.match(/detail\/(\d+)&([a-f0-9-]+)&(\d+)\/(\d+)\/(\d+)/);
            if (oldMatch) {
                console.log(`[CbeadProgressManager] ⚠️ 使用旧 URL 格式提取参数(兼容模式)`);
                return {
                    courseId: oldMatch[1],
                    uuid: oldMatch[2],
                    sectionIndex: oldMatch[3],
                    totalSections: oldMatch[4],
                    currentIndex: oldMatch[5]
                };
            }
            console.warn(`[CbeadProgressManager] ⚠️ 无法从 URL 提取参数: ${hash}`);
            return null;
        }
    };
    class IntervalManager {
        constructor() {
            this.intervals = new Set;
            this.timeouts = new Set;
        }
        setInterval(callback, delay) {
            const id = setInterval(callback, delay);
            this.intervals.add(id);
            return id;
        }
        setTimeout(callback, delay) {
            const id = setTimeout(callback, delay);
            this.timeouts.add(id);
            return id;
        }
        clearInterval(id) {
            clearInterval(id);
            this.intervals.delete(id);
        }
        clearTimeout(id) {
            clearTimeout(id);
            this.timeouts.delete(id);
        }
        clearAll() {
            this.intervals.forEach(id => clearInterval(id));
            this.intervals.clear();
            this.timeouts.forEach(id => clearTimeout(id));
            this.timeouts.clear();
            console.log("[CbeadPlayer] ✅ 已清理所有定时器");
        }
        getCounts() {
            return {
                intervals: this.intervals.size,
                timeouts: this.timeouts.size
            };
        }
    }
    const CbeadPlayer = {
        DEBUG: false,
        _debugLog(...args) {
            if (this.DEBUG) {
                console.log(...args);
            }
        },
        detectVideoPlayer() {
            const videoJsPlayer = document.querySelector("video.vjs-tech");
            if (videoJsPlayer) {
                console.log("[CbeadPlayer] 检测到 Video.js 播放器");
                const videoJsInstance = window.player || videoJsPlayer.player && videoJsPlayer.player;
                return {
                    type: "videojs",
                    element: videoJsPlayer,
                    player: videoJsInstance,
                    getCurrentTime: () => videoJsPlayer.currentTime,
                    setCurrentTime: time => {
                        videoJsPlayer.currentTime = time;
                    },
                    getDuration: () => videoJsPlayer.duration,
                    play: () => videoJsPlayer.play(),
                    pause: () => videoJsPlayer.pause(),
                    setMuted: muted => {
                        videoJsPlayer.muted = muted;
                        if (videoJsInstance && typeof videoJsInstance.muted === "function") {
                            try {
                                videoJsInstance.muted(muted);
                            } catch (e) {
                                console.warn("[CbeadPlayer] Video.js实例静音失败:", e);
                            }
                        }
                        if (muted) {
                            videoJsPlayer.volume = 0;
                        }
                    },
                    getMuted: () => videoJsPlayer.muted
                };
            }
            const genericVideo = document.querySelector("video");
            if (genericVideo) {
                console.log("[CbeadPlayer] 使用通用 video 元素");
                return {
                    type: "generic",
                    element: genericVideo,
                    getCurrentTime: () => genericVideo.currentTime,
                    setCurrentTime: time => {
                        genericVideo.currentTime = time;
                    },
                    getDuration: () => genericVideo.duration,
                    play: () => genericVideo.play(),
                    pause: () => genericVideo.pause(),
                    setMuted: muted => {
                        genericVideo.muted = muted;
                    },
                    getMuted: () => genericVideo.muted
                };
            }
            console.warn("[CbeadPlayer] 未找到视频播放器");
            return null;
        },
        parseTimeToSeconds(timeStr) {
            if (!timeStr) return 0;
            const match = timeStr.match(/(\d+):(\d+)/);
            if (!match) return 0;
            const minutes = parseInt(match[1]);
            const seconds = parseInt(match[2]);
            return minutes * 60 + seconds;
        },
        extractChapterProgress(verbose = true) {
            const catalogSelectors = [ ".course-side-catalog", ".new-course-side-catalog", ".course-catalog", '[class*="side-catalog"]', '[class*="catalog"]' ];
            let catalog = null;
            for (const selector of catalogSelectors) {
                catalog = document.querySelector(selector);
                if (catalog) {
                    if (verbose) console.log(`[CbeadPlayer] 使用目录选择器: ${selector}`);
                    break;
                }
            }
            if (!catalog) {
                const allElements = document.querySelectorAll("*");
                for (const el of allElements) {
                    if (el.className && typeof el.className === "string" && el.className.includes("catalog")) {
                        catalog = el;
                        if (verbose) console.log(`[CbeadPlayer] 通过类名找到目录: ${el.className}`);
                        break;
                    }
                }
            }
            if (!catalog) {
                if (verbose) console.warn("[CbeadPlayer] 未找到章节目录");
                return null;
            }
            const boxSelectors = [ ".chapter-list-box", '[class*="chapter-list-box"]', ".chapter-list", '[class*="chapter-box"]' ];
            let chapterBoxes = null;
            for (const selector of boxSelectors) {
                chapterBoxes = catalog.querySelectorAll(selector);
                if (chapterBoxes.length > 0) {
                    if (verbose) console.log(`[CbeadPlayer] 使用章节框选择器: ${selector}`);
                    break;
                }
            }
            if (!chapterBoxes || chapterBoxes.length === 0) {
                const jspPane = catalog.querySelector(".jspPane");
                if (jspPane) {
                    chapterBoxes = jspPane.querySelectorAll(".chapter-list-box");
                    if (chapterBoxes.length > 0) {
                        if (verbose) console.log("[CbeadPlayer] 从 .jspPane 中找到章节框");
                    }
                }
            }
            if (!chapterBoxes || chapterBoxes.length === 0) {
                const allBoxes = catalog.querySelectorAll('[class*="chapter"]');
                const validBoxes = Array.from(allBoxes).filter(box => box.querySelector(".item-completed") || box.querySelector(".item.sub.item-completed") || box.textContent?.includes("完成率"));
                if (validBoxes.length > 0) {
                    chapterBoxes = validBoxes;
                    if (verbose) console.log(`[CbeadPlayer] 通过 item-completed 筛选找到 ${chapterBoxes.length} 个章节`);
                }
            }
            if (!chapterBoxes || chapterBoxes.length === 0) {
                if (verbose) console.warn("[CbeadPlayer] 未找到章节元素");
                return null;
            }
            const chapters = [];
            chapterBoxes.forEach((box, index) => {
                try {
                    const titleEl = box.querySelector(".chapter-item .text-overflow");
                    const title = titleEl?.textContent?.trim() || `章节${index + 1}`;
                    this._debugLog(`\n[调试] 处理章节 ${index + 1}: ${title}`);
                    const allItems = box.querySelectorAll(".item");
                    this._debugLog(`  [调试] 找到 ${allItems.length} 个 .item 元素:`);
                    allItems.forEach((el, i) => {
                        const cls = el.className || "";
                        const text = el.textContent?.trim() || "";
                        this._debugLog(`    [调试] .item[${i}]: class="${cls}", text="${text}"`);
                    });
                    const statusEls = box.querySelectorAll(".item.item22");
                    this._debugLog(`  [调试] .item.item22 数量: ${statusEls.length}`);
                    const statusEl = statusEls[statusEls.length - 1];
                    const statusText = statusEl?.textContent?.trim() || "";
                    this._debugLog(`  [调试] statusText: "${statusText}"`);
                    const completedEl = box.querySelector(".item-completed") || box.querySelector(".item.sub.item-completed") || box.querySelector('[class*="item-completed"]') || box.querySelector(".item.sub");
                    const completedText = completedEl?.textContent?.trim() || "";
                    const progressMatch = completedText.match(/完成率[::]\s*(\d+)%/);
                    const progress = progressMatch ? parseInt(progressMatch[1]) : 0;
                    this._debugLog(`  [调试] completedEl: ${completedEl ? "找到" : "未找到"}`);
                    this._debugLog(`  [调试] completedText: "${completedText}"`);
                    this._debugLog(`  [调试] progress: ${progress}%`);
                    let status = "unknown";
                    let isCompleted = false;
                    const normalizedStatusText = statusText.trim();
                    if (normalizedStatusText === "已完成") {
                        status = "completed";
                        isCompleted = true;
                    } else if (normalizedStatusText === "学习中") {
                        status = "in_progress";
                        isCompleted = false;
                    } else if (normalizedStatusText === "未开始") {
                        status = "not_started";
                        isCompleted = false;
                    } else {
                        if (progress > 0) {
                            status = "in_progress";
                            isCompleted = false;
                        } else {
                            status = "not_started";
                            isCompleted = false;
                        }
                    }
                    const statusInfo = {
                        completed: {
                            icon: "✅",
                            text: "已完成"
                        },
                        in_progress: {
                            icon: "📖",
                            text: "学习中"
                        },
                        not_started: {
                            icon: "📝",
                            text: "未开始"
                        },
                        unknown: {
                            icon: "❓",
                            text: "未知"
                        }
                    };
                    const statusDisplay = statusInfo[status] || statusInfo["unknown"];
                    chapters.push({
                        index: index + 1,
                        title: title,
                        status: status,
                        statusText: statusText,
                        progress: progress,
                        isCompleted: isCompleted,
                        element: box
                    });
                    if (verbose) {
                        console.log(`  ${statusDisplay.icon} 章节 ${index + 1}: ${title} - ${statusDisplay.text} (${progress}%)`);
                    }
                } catch (error) {
                    if (verbose) console.error(`[CbeadPlayer] 处理章节失败 (索引 ${index}):`, error);
                }
            });
            if (chapters.length === 0) {
                if (verbose) console.warn("[CbeadPlayer] 未找到章节");
                return null;
            }
            const stats = {
                completed: chapters.filter(ch => ch.status === "completed").length,
                inProgress: chapters.filter(ch => ch.status === "in_progress").length,
                notStarted: chapters.filter(ch => ch.status === "not_started").length
            };
            const firstIncomplete = chapters.find(ch => !ch.isCompleted);
            if (verbose) {
                console.log(`[CbeadPlayer] 找到 ${chapters.length} 个章节:`);
                console.log(`  - ✅ 已完成: ${stats.completed} 门`);
                console.log(`  - 📖 学习中: ${stats.inProgress} 门`);
                console.log(`  - 📝 未开始: ${stats.notStarted} 门`);
                if (firstIncomplete) {
                    const statusInfo = {
                        completed: {
                            icon: "✅",
                            text: "已完成"
                        },
                        in_progress: {
                            icon: "📖",
                            text: "学习中"
                        },
                        not_started: {
                            icon: "📝",
                            text: "未开始"
                        }
                    };
                    const statusDisplay = statusInfo[firstIncomplete.status] || {
                        text: "未知"
                    };
                    console.log(`[CbeadPlayer] 💡 当前章节: ${firstIncomplete.index} - ${statusDisplay.text} (${firstIncomplete.progress}%)`);
                }
            }
            return {
                total: chapters.length,
                completed: stats.completed,
                inProgress: stats.inProgress,
                notStarted: stats.notStarted,
                chapters: chapters,
                firstIncomplete: firstIncomplete ?? null,
                allCompleted: firstIncomplete == null
            };
        },
        isCourseReallyCompleted() {
            const chapterProgress = this.extractChapterProgress();
            if (!chapterProgress) {
                console.warn("[CbeadPlayer] 无法判断章节进度,假设未完成");
                return false;
            }
            const isCompleted = chapterProgress.allCompleted;
            if (isCompleted) {
                console.log(`[CbeadPlayer] ✅ 所有章节已完成 (${chapterProgress.completed}/${chapterProgress.total})`);
            } else {
                const first = chapterProgress.firstIncomplete;
                if (first) {
                    console.log(`[CbeadPlayer] 📖 章节 ${first.index} 未完成 (${first.status}, ${first.progress}%)`);
                    console.log(`[CbeadPlayer] 💡 判断标准:章节右侧的"已完成"标记,而非完成率百分比`);
                } else {
                    console.warn(`[CbeadPlayer] ⚠️ 章节状态异常: firstIncomplete为null但allCompleted为false`);
                    console.log(`[CbeadPlayer] 📊 统计信息: ${chapterProgress.completed}已完成/${chapterProgress.total}总计`);
                    return chapterProgress.completed >= chapterProgress.total;
                }
            }
            return isCompleted;
        },
        extractChapterList() {
            const catalog = document.querySelector(".course-side-catalog");
            if (!catalog) {
                console.warn("[CbeadPlayer] 未找到章节目录");
                return [];
            }
            const chapters = [];
            const chapterBoxes = catalog.querySelectorAll(".chapter-list-box");
            console.log(`[CbeadPlayer] 找到 ${chapterBoxes.length} 个章节`);
            chapterBoxes.forEach((box, index) => {
                try {
                    const titleEl = box.querySelector(".chapter-item .text-overflow");
                    const title = titleEl?.textContent?.trim() || `第${index + 1}章`;
                    const sections = box.querySelectorAll(".section-item-wrapper");
                    const sectionList = [];
                    sections.forEach(section => {
                        const durationText = section.querySelector(".section-item .item:last-child")?.textContent?.trim();
                        const duration = this.parseTimeToSeconds(durationText);
                        const completedEl = section.closest(".chapter-list-box")?.querySelector(".item-completed");
                        const completedText = completedEl?.textContent?.trim() || "";
                        const progressMatch = completedText.match(/完成率[::]\s*(\d+)%/);
                        const progress = progressMatch ? parseInt(progressMatch[1]) : 0;
                        sectionList.push({
                            title: `${title} - 节`,
                            duration: duration,
                            progress: progress,
                            isCompleted: progress >= 100
                        });
                    });
                    chapters.push({
                        title: title,
                        sections: sectionList
                    });
                    console.log(`[CbeadPlayer] 章节 ${index + 1}: ${title} (${sectionList.length} 节)`);
                } catch (error) {
                    console.error(`[CbeadPlayer] 处理章节失败 (索引 ${index}):`, error);
                }
            });
            console.log(`[CbeadPlayer] 成功提取 ${chapters.length} 个章节`);
            return chapters;
        },
        clickChapter(chapterTitle) {
            try {
                console.log(`[CbeadPlayer] 🔍 查找章节: ${chapterTitle}`);
                const catalog = document.querySelector(".course-side-catalog");
                if (!catalog) {
                    console.warn("[CbeadPlayer] 未找到章节目录");
                    return false;
                }
                const chapterBoxes = catalog.querySelectorAll(".chapter-list-box");
                for (const box of chapterBoxes) {
                    const titleEl = box.querySelector(".chapter-item .text-overflow");
                    const title = titleEl?.textContent?.trim();
                    if (title === chapterTitle || title?.includes(chapterTitle)) {
                        console.log(`[CbeadPlayer] ✅ 找到目标章节: ${title}`);
                        box.click();
                        const playBtn = box.querySelector(".section-item");
                        if (playBtn) {
                            playBtn.click();
                        }
                        return true;
                    }
                }
                console.warn(`[CbeadPlayer] 未找到章节: ${chapterTitle}`);
                return false;
            } catch (error) {
                console.error(`[CbeadPlayer] 点击章节失败:`, error);
                return false;
            }
        },
        async waitForPlayerReady(maxWaitTime = CBEAD_CONSTANTS.TIMING.PLAYER_INIT_TIMEOUT, checkInterval = CBEAD_CONSTANTS.TIMING.PLAYER_CHECK_INTERVAL) {
            const startTime = Date.now();
            const manager = new IntervalManager;
            console.log("[CbeadPlayer] ⏳ 等待播放器准备就绪...");
            console.log(`[CbeadPlayer] 📋 配置: 最大等待=${maxWaitTime}ms, 检查间隔=${checkInterval}ms`);
            return new Promise(resolve => {
                manager.setInterval(() => {
                    try {
                        const elapsed = Date.now() - startTime;
                        const player = this.detectVideoPlayer();
                        if (player) {
                            const duration = player.getDuration();
                            const currentTime = player.getCurrentTime();
                            const isMuted = player.getMuted();
                            console.log(`[CbeadPlayer] 🔍 检查 #${Math.floor(elapsed / checkInterval)}: 播放器=${player.type}, duration=${duration}s, currentTime=${currentTime}s, muted=${player.getMuted()}`);
                            if (duration && duration > 0 && isFinite(duration)) {
                                manager.clearAll();
                                console.log(`[CbeadPlayer] ✅ 播放器已就绪 (等待 ${elapsed}ms)`);
                                console.log(`[CbeadPlayer] 📹 播放器详情: type=${player.type}, duration=${Math.round(duration)}s, currentTime=${Math.round(currentTime)}s`);
                                resolve(player);
                                return;
                            } else {
                                console.log(`[CbeadPlayer] ⏳ duration 未就绪: ${duration} (需要 > 0)`);
                            }
                        } else {
                            console.log(`[CbeadPlayer] ⏳ 检查 #${Math.floor(elapsed / checkInterval)}: 未检测到播放器`);
                        }
                        if (elapsed >= maxWaitTime) {
                            manager.clearAll();
                            console.warn(`[CbeadPlayer] ⚠️ 播放器等待超时 (${maxWaitTime}ms)`);
                            resolve(null);
                            return;
                        }
                    } catch (error) {
                        console.error("[CbeadPlayer] 播放器检测异常:", error);
                        manager.clearAll();
                        resolve(null);
                    }
                }, checkInterval);
                manager.setTimeout(() => {
                    if (manager.getCounts().intervals > 0) {
                        console.warn("[CbeadPlayer] ⏰ 超时保护触发,强制清理定时器");
                        manager.clearAll();
                        resolve(null);
                    }
                }, maxWaitTime + CBEAD_CONSTANTS.TIMING.PROTECTION_TIMEOUT);
            });
        },
        getCurrentChapterName() {
            try {
                const chapterProgress = this.extractChapterProgress(false);
                if (chapterProgress && chapterProgress.chapters && chapterProgress.chapters.length > 0) {
                    return chapterProgress.chapters[0].title;
                }
            } catch (error) {}
            return null;
        },
        createIntervalManager() {
            return new IntervalManager;
        },
        getServerProgress(currentChapterIndex = null) {
            try {
                const chapterProgress = this.extractChapterProgress(false);
                if (!chapterProgress) return null;
                if (currentChapterIndex !== null) {
                    const currentChapter = chapterProgress.chapters.find(ch => ch.index === currentChapterIndex);
                    if (currentChapter) return currentChapter.progress;
                }
                if (chapterProgress.firstIncomplete) {
                    return chapterProgress.firstIncomplete.progress;
                }
                return null;
            } catch (e) {
                return null;
            }
        },
        cleanupPlaybackResources(resources, reason = "未知原因") {
            const {wakeLock: wakeLock, handleVisibilityChange: handleVisibilityChange, timerManager: timerManager} = resources;
            console.log(`[CbeadPlayer] 🧹 清理播放资源 (${reason})`);
            if (wakeLock) {
                try {
                    wakeLock.release();
                    console.log("[CbeadPlayer] 📱 已释放屏幕常亮锁");
                } catch (err) {
                    console.warn("[CbeadPlayer] ⚠️ 释放 Wake Lock 失败:", err);
                }
            }
            if (handleVisibilityChange) {
                try {
                    document.removeEventListener("visibilitychange", handleVisibilityChange);
                    console.log("[CbeadPlayer] 🧹 已移除可见性监听器");
                } catch (err) {
                    console.warn("[CbeadPlayer] ⚠️ 移除可见性监听器失败:", err);
                }
            }
            if (timerManager) {
                try {
                    timerManager.clearAll();
                } catch (err) {
                    console.warn("[CbeadPlayer] ⚠️ 清理定时器失败:", err);
                }
            }
            console.log("[CbeadPlayer] ✅ 资源清理完成");
        }
    };
    const WORKER_CODE = `\n  let timer = null;\n  let config = null;\n\n  self.onmessage = async function(e) {\n    const { action, data } = e.data;\n\n    if (action === 'start') {\n      config = data;\n      console.log('[CbeadHeartbeatWorker] 🚀 Worker 启动,心跳间隔:', config?.interval || 10000, 'ms');\n\n      timer = setInterval(async () => {\n        try {\n          // 【TODO】后续补充企业分院 API 调用\n          // 目前仅发送心跳消息保活 Worker 框架\n          // 浦东分院 API (/inc/nc/course/play/pulseSaveRecord) 在企业分院无效\n          // 企业分院专用 API 待探索\n\n          // 示例调用(待实现):\n          // const result = await sendHeartbeat({\n          //   courseId: config.courseId,\n          //   chapterId: config.chapterId,\n          //   timestamp: Date.now()\n          // });\n\n          self.postMessage({\n            type: 'heartbeat',\n            timestamp: Date.now(),\n            config: config\n          });\n        } catch (err) {\n          self.postMessage({\n            type: 'error',\n            error: err.message,\n            timestamp: Date.now()\n          });\n        }\n      }, config?.interval || 10000);\n\n    } else if (action === 'stop') {\n      if (timer) {\n        clearInterval(timer);\n        timer = null;\n      }\n      config = null;\n      console.log('[CbeadHeartbeatWorker] 🛑 Worker 已停止');\n\n    } else if (action === 'ping') {\n      self.postMessage({\n        type: 'pong',\n        timestamp: Date.now(),\n        config: config\n      });\n    }\n  };\n`;
    const CbeadHeartbeatWorker = {
        createWorker() {
            const blob = new Blob([ WORKER_CODE ], {
                type: "application/javascript"
            });
            const workerUrl = URL.createObjectURL(blob);
            return new Worker(workerUrl);
        },
        start(courseInfo) {
            const worker = this.createWorker();
            worker.onmessage = e => {
                const {type: type, timestamp: timestamp, config: config} = e.data;
                switch (type) {
                  case "heartbeat":
                    console.log(`[CbeadHeartbeatWorker] 💓 心跳 ${timestamp} - 课程: ${config?.courseId || "N/A"}`);
                    break;

                  case "error":
                    console.error(`[CbeadHeartbeatWorker] ❌ 心跳错误: ${e.data.error}`);
                    break;

                  case "pong":
                    console.log(`[CbeadHeartbeatWorker] 🔵 Pong ${timestamp}`);
                    break;

                  default:
                    console.log(`[CbeadHeartbeatWorker] 📨 消息:`, e.data);
                }
            };
            worker.onerror = error => {
                console.error(`[CbeadHeartbeatWorker] 💥 Worker 错误:`, error);
            };
            worker.postMessage({
                action: "start",
                data: {
                    courseId: courseInfo.courseId,
                    chapterId: courseInfo.chapterId,
                    interval: courseInfo.interval || 1e4
                }
            });
            console.log(`[CbeadHeartbeatWorker] ✅ Worker 已启动`);
            return worker;
        },
        stop(worker) {
            if (worker) {
                worker.postMessage({
                    action: "stop"
                });
                worker.terminate();
                console.log(`[CbeadHeartbeatWorker] 🛑 Worker 已终止`);
            }
        },
        async ping(worker) {
            if (!worker) return false;
            return new Promise(resolve => {
                const timeout = setTimeout(() => {
                    resolve(false);
                }, 2e3);
                worker.onmessage = e => {
                    if (e.data.type === "pong") {
                        clearTimeout(timeout);
                        resolve(true);
                    }
                };
                worker.postMessage({
                    action: "ping"
                });
            });
        }
    };
    function debugLog(...args) {}
    const CBEAD_API_CONFIG = {
        baseUrl: null,
        endpoints: {
            GET_PLAY_TREND: "/inc/nc/course/play/getPlayTrend",
            GET_COURSE_LIST: "/inc/nc/course/getCourseList",
            PULSE_SAVE_RECORD: "/inc/nc/course/play/pulseSaveRecord",
            GET_STUDY_RECORD: "/inc/nc/course/getStudyRecord",
            GET_COURSEWARE_DETAIL: "/inc/nc/course/play/getCoursewareDetail",
            GET_PACK_BY_ID: "/inc/nc/course/pd/getPackById"
        },
        getBaseUrl() {
            const config = ServiceLocator.get(ServiceNames.CONFIG);
            this.baseUrl = config?.CBEAD_API_BASE || `https://${window.location.hostname}`;
            return this.baseUrl;
        },
        getUrl(endpoint) {
            const baseUrl = this.getBaseUrl();
            const endpointPath = this.endpoints[endpoint] || endpoint;
            return baseUrl + endpointPath;
        }
    };
    function _buildUrl(endpoint, params = {}) {
        let url = CBEAD_API_CONFIG.getUrl(endpoint);
        const queryString = new URLSearchParams({
            ...params,
            _t: Date.now()
        }).toString();
        return `${url}?${queryString}`;
    }
    const CbeadApi = {
        ...ApiCore,
        isSuccessResponse(result) {
            return result && (result.success === true || result.code === 200 || result.code === 2e4 || result.state === 2e4 || result.status === "success" || result.status === "ok" || result.code >= 200 && result.code < 300 || result.result === "success" || result.success === 1);
        },
        async getPlayInfo(courseId, coursewareId = null, courseDuration = null) {
            const {Utils: Utils} = await Promise.resolve().then(function() {
                return utils;
            });
            try {
                debugLog(`[getPlayInfo] 获取课程 ${courseId} 的播放信息`);
                const response = await this.get(_buildUrl("GET_PLAY_TREND", {
                    courseId: courseId
                }));
                if (response?.success && response?.data) {
                    const data = response.data;
                    let videoId = null;
                    let duration = 0;
                    let lastLearnedTime = 0;
                    let foundCoursewareId = coursewareId;
                    if (coursewareId && data.playTree?.children) {
                        const target = data.playTree.children.find(c => String(c.id) === String(coursewareId));
                        if (target) {
                            videoId = target.id;
                            foundCoursewareId = target.id;
                            duration = target.sumDurationLong || 0;
                            lastLearnedTime = target.lastWatchPoint ? Utils.parseTimeToSeconds(target.lastWatchPoint) : 0;
                            debugLog(`成功匹配到课件: ${target.title}`);
                        }
                    }
                    if (!videoId && data.locationSite) {
                        videoId = data.locationSite.id;
                        foundCoursewareId = data.locationSite.id;
                        duration = data.locationSite.sumDurationLong || 0;
                        lastLearnedTime = data.locationSite.lastWatchPoint ? Utils.parseTimeToSeconds(data.locationSite.lastWatchPoint) : 0;
                    }
                    if (duration === 0 && courseDuration) {
                        duration = Utils.parseDuration(courseDuration);
                    }
                    if (duration === 0) {
                        duration = CONSTANTS.TIME_FORMATS.DEFAULT_DURATION;
                    }
                    if (!videoId) {
                        videoId = `mock_video_${courseId}`;
                        debugLog("无法获取真实 videoId,使用模拟ID");
                    }
                    return {
                        courseId: courseId,
                        coursewareId: foundCoursewareId,
                        videoId: videoId,
                        duration: duration,
                        lastLearnedTime: lastLearnedTime,
                        playURL: `https://zpyapi.shsets.com/player/get?videoId=${videoId}`,
                        dataSource: videoId.startsWith("mock_") ? "fallback" : "api"
                    };
                }
                return null;
            } catch (error) {
                debugLog(`[getPlayInfo] 出错: ${error.message}`);
                return null;
            }
        },
        async reportProgress(playInfo, currentTime) {
            const {Utils: Utils} = await Promise.resolve().then(function() {
                return utils;
            });
            const watchPoint = Utils.formatTime(currentTime);
            const progress = Math.round(currentTime / playInfo.duration * 100);
            const payload = new URLSearchParams({
                courseId: playInfo.courseId,
                coursewareId: playInfo.coursewareId || playInfo.videoId,
                videoId: playInfo.videoId || "",
                watchPoint: watchPoint,
                currentTime: currentTime,
                duration: playInfo.duration,
                progress: progress,
                pulseTime: 10,
                pulseRate: 1,
                _t: Date.now()
            }).toString();
            try {
                return await this.post(_buildUrl("PULSE_SAVE_RECORD"), payload, {
                    headers: {
                        "Content-Type": "application/x-www-form-urlencoded"
                    }
                });
            } catch (error) {
                debugLog(`[进度同步] 失败: ${error.message}`);
                throw error;
            }
        },
        async checkCompletion(courseId, coursewareId = null) {
            try {
                const response = await this.get(_buildUrl("GET_PLAY_TREND", {
                    courseId: courseId
                }));
                const config = ServiceLocator.get(ServiceNames.CONFIG);
                if (response?.success && response?.data) {
                    const data = response.data;
                    if (coursewareId && data.playTree?.children) {
                        const target = data.playTree.children.find(c => String(c.id) === String(coursewareId));
                        if (target) {
                            const finishedRate = parseInt(target.finishedRate || 0);
                            return {
                                isCompleted: finishedRate >= (config?.COMPLETION_THRESHOLD || 80),
                                finishedRate: finishedRate,
                                method: "playTree_match"
                            };
                        }
                    }
                    if (data.locationSite && data.locationSite.finishedRate !== undefined) {
                        const finishedRate = parseInt(data.locationSite.finishedRate);
                        return {
                            isCompleted: finishedRate >= (config?.COMPLETION_THRESHOLD || 80),
                            finishedRate: finishedRate,
                            method: "playTrend_total"
                        };
                    }
                }
                return {
                    isCompleted: false,
                    finishedRate: 0,
                    method: "default"
                };
            } catch (error) {
                debugLog(`完成度检查失败: ${error.message}`);
                return {
                    isCompleted: false,
                    finishedRate: 0,
                    method: "error"
                };
            }
        },
        async getCoursewareList(courseId) {
            try {
                debugLog(`正在获取课程包详细信息 (ID: ${courseId})...`);
                const endpoints = [ _buildUrl("GET_PLAY_TREND", {
                    courseId: courseId
                }), _buildUrl("GET_COURSEWARE_DETAIL", {
                    courseId: courseId
                }) ];
                for (const endpoint of endpoints) {
                    try {
                        const response = await this.get(endpoint);
                        if (response && response.success && response.data) {
                            const data = response.data;
                            if (data.playTree && data.playTree.children && Array.isArray(data.playTree.children)) {
                                const videos = data.playTree.children.filter(c => c.rTypeValue === "video" || c.rTypeValue === "courseware");
                                if (videos.length > 0) {
                                    debugLog(`从 playTree 获取到 ${videos.length} 个课件`);
                                    return videos.map((v, index) => CourseAdapter.normalize({
                                        id: courseId,
                                        courseId: courseId,
                                        dsUnitId: v.id,
                                        title: v.title || `${data.title || "课程"} - 视频${index + 1}`,
                                        duration: v.sumDurationLong || 0
                                    }, "cbead_api_tree"));
                                }
                            }
                            if (data.coursewareIdList && Array.isArray(data.coursewareIdList) && data.coursewareIdList.length > 0) {
                                debugLog(`从 coursewareIdList 获取到 ${data.coursewareIdList.length} 个课件`);
                                return data.coursewareIdList.map((cw, index) => CourseAdapter.normalize({
                                    id: courseId,
                                    courseId: courseId,
                                    dsUnitId: cw.id || cw.coursewareId,
                                    title: cw.name || cw.title || `${data.title || "课程"} - 视频${index + 1}`,
                                    duration: cw.duration || 0
                                }, "cbead_api_list"));
                            }
                            const list = data.subList || data.courseList || data.lessons;
                            if (list && Array.isArray(list) && list.length > 0) {
                                debugLog(`从 API 子列表获取到 ${list.length} 个视频`);
                                return list.map(item => CourseAdapter.normalize(item, "cbead_api_sublist"));
                            }
                        }
                    } catch {
                        continue;
                    }
                }
                return [];
            } catch (error) {
                debugLog(`获取课件列表失败: ${error.message}`);
                return [];
            }
        },
        async getPackById(packId) {
            try {
                debugLog(`获取课程包信息 (ID: ${packId})`);
                return await this.get(_buildUrl("GET_PACK_BY_ID", {
                    id: packId
                }));
            } catch (error) {
                debugLog(`获取课程包信息失败: ${error.message}`);
                return null;
            }
        }
    };
    const LOG_LEVELS = {
        DEBUG: 0,
        INFO: 1,
        WARN: 2,
        ERROR: 3,
        NONE: 4
    };
    const defaultConfig$1 = {
        level: LOG_LEVELS.INFO,
        showTimestamp: true,
        showModule: true,
        disableInProduction: true
    };
    let config$1 = {
        ...defaultConfig$1
    };
    function getTimestamp() {
        const now = new Date;
        return now.toISOString().split("T")[1].replace("Z", "").slice(0, -1);
    }
    function formatMessage(level, module, args) {
        const parts = [];
        if (config$1.showTimestamp) {
            parts.push(`[${getTimestamp()}]`);
        }
        parts.push(`[${level}]`);
        if (config$1.showModule && module) {
            parts.push(`[${module}]`);
        }
        return [ parts.join(" "), ...args ];
    }
    const Logger = {
        setLevel(level) {
            if (typeof level === "string") {
                const upperLevel = level.toUpperCase();
                config$1.level = LOG_LEVELS[upperLevel] !== undefined ? LOG_LEVELS[upperLevel] : LOG_LEVELS.INFO;
            } else if (typeof level === "number") {
                config$1.level = level >= 0 && level <= 4 ? level : LOG_LEVELS.INFO;
            }
            if (typeof window !== "undefined") {
                const isDev = window.location?.hostname?.includes("localhost") || window.location?.hostname?.includes("dev");
                if (isDev && config$1.level === LOG_LEVELS.INFO) {
                    config$1.level = LOG_LEVELS.DEBUG;
                }
            }
        },
        getLevel() {
            return config$1.level;
        },
        debug(...args) {
            if (config$1.level <= LOG_LEVELS.DEBUG) {
                console.debug(...formatMessage("DEBUG", null, args));
            }
        },
        debugM(module, ...args) {
            if (config$1.level <= LOG_LEVELS.DEBUG) {
                console.debug(...formatMessage("DEBUG", module, args));
            }
        },
        info(...args) {
            if (config$1.level <= LOG_LEVELS.INFO) {
                console.info(...formatMessage("INFO", null, args));
            }
        },
        infoM(module, ...args) {
            if (config$1.level <= LOG_LEVELS.INFO) {
                console.info(...formatMessage("INFO", module, args));
            }
        },
        warn(...args) {
            if (config$1.level <= LOG_LEVELS.WARN) {
                console.warn(...formatMessage("WARN", null, args));
            }
        },
        warnM(module, ...args) {
            if (config$1.level <= LOG_LEVELS.WARN) {
                console.warn(...formatMessage("WARN", module, args));
            }
        },
        error(...args) {
            if (config$1.level <= LOG_LEVELS.ERROR) {
                console.error(...formatMessage("ERROR", null, args));
            }
        },
        errorM(module, ...args) {
            if (config$1.level <= LOG_LEVELS.ERROR) {
                console.error(...formatMessage("ERROR", module, args));
            }
        },
        log(level, ...args) {
            const upperLevel = level.toUpperCase();
            const levelValue = LOG_LEVELS[upperLevel] !== undefined ? LOG_LEVELS[upperLevel] : LOG_LEVELS.INFO;
            if (config$1.level <= levelValue) {
                console.log(...formatMessage(upperLevel, null, args));
            }
        },
        group(label, collapsed = false) {
            if (config$1.level <= LOG_LEVELS.DEBUG) {
                if (collapsed) {
                    console.groupCollapsed(label);
                } else {
                    console.group(label);
                }
            }
        },
        groupEnd() {
            if (config$1.level <= LOG_LEVELS.DEBUG) {
                console.groupEnd();
            }
        },
        time(label) {
            if (config$1.level <= LOG_LEVELS.DEBUG) {
                console.time(label);
            }
        },
        timeEnd(label) {
            if (config$1.level <= LOG_LEVELS.DEBUG) {
                console.timeEnd(label);
            }
        },
        table(label, data) {
            if (config$1.level <= LOG_LEVELS.DEBUG && Array.isArray(data)) {
                this.group(label);
                console.table(data);
                this.groupEnd();
            }
        },
        configure(newConfig) {
            config$1 = {
                ...config$1,
                ...newConfig
            };
        },
        reset() {
            config$1 = {
                ...defaultConfig$1
            };
        }
    };
    function createLogger(moduleName) {
        return {
            debug: (...args) => Logger.debugM(moduleName, ...args),
            info: (...args) => Logger.infoM(moduleName, ...args),
            warn: (...args) => Logger.warnM(moduleName, ...args),
            error: (...args) => Logger.errorM(moduleName, ...args),
            group: (label, collapsed) => Logger.group(`[${moduleName}] ${label}`, collapsed),
            time: label => Logger.time(`[${moduleName}] ${label}`),
            timeEnd: label => Logger.timeEnd(`[${moduleName}] ${label}`)
        };
    }
    const WAIT_LOGGER = createLogger("WaitHelper");
    const defaultConfig = {
        elementTimeout: 1e4,
        stableTimeout: 2e3,
        randomMin: 500,
        randomMax: 2e3,
        observerInterval: 100,
        retryCount: 3,
        retryDelay: 1e3
    };
    let config = {
        ...defaultConfig
    };
    const WaitHelper = {
        async forElement(selector, timeout = config.elementTimeout) {
            const existing = document.querySelector(selector);
            if (existing) {
                WAIT_LOGGER.debug(`元素已存在: ${selector}`);
                return existing;
            }
            WAIT_LOGGER.debug(`等待元素: ${selector} (超时: ${timeout}ms)`);
            return new Promise((resolve, _reject) => {
                const observer = new MutationObserver(() => {
                    const el = document.querySelector(selector);
                    if (el) {
                        observer.disconnect();
                        WAIT_LOGGER.debug(`找到元素: ${selector}`);
                        resolve(el);
                    }
                });
                observer.observe(document.body, {
                    childList: true,
                    subtree: true
                });
                setTimeout(() => {
                    observer.disconnect();
                    const el = document.querySelector(selector);
                    if (el) {
                        resolve(el);
                    } else {
                        WAIT_LOGGER.warn(`等待元素超时: ${selector}`);
                        resolve(null);
                    }
                }, timeout);
            });
        },
        async forElements(selector, minCount = 1, timeout = config.elementTimeout) {
            const checkElements = () => {
                const elements = document.querySelectorAll(selector);
                return elements.length >= minCount ? elements : null;
            };
            const existing = checkElements();
            if (existing) {
                WAIT_LOGGER.debug(`元素已满足条件: ${selector} (${existing.length} 个)`);
                return existing;
            }
            WAIT_LOGGER.debug(`等待至少 ${minCount} 个元素: ${selector}`);
            return new Promise(resolve => {
                const observer = new MutationObserver(() => {
                    const elements = checkElements();
                    if (elements) {
                        observer.disconnect();
                        WAIT_LOGGER.debug(`找到 ${elements.length} 个元素: ${selector}`);
                        resolve(elements);
                    }
                });
                observer.observe(document.body, {
                    childList: true,
                    subtree: true
                });
                setTimeout(() => {
                    observer.disconnect();
                    const elements = checkElements();
                    if (elements) {
                        resolve(elements);
                    } else {
                        WAIT_LOGGER.warn(`等待元素超时: ${selector}`);
                        resolve(null);
                    }
                }, timeout);
            });
        },
        async forStable(timeout = config.stableTimeout) {
            WAIT_LOGGER.debug(`等待页面稳定 (${timeout}ms)`);
            let stableTime = 0;
            let lastState = document.body?.innerHTML?.length || 0;
            return new Promise(resolve => {
                const observer = new MutationObserver(() => {
                    stableTime = 0;
                    lastState = document.body?.innerHTML?.length || 0;
                });
                if (document.body) {
                    observer.observe(document.body, {
                        childList: true,
                        subtree: true,
                        attributes: true,
                        characterData: true
                    });
                }
                const interval = setInterval(() => {
                    const currentState = document.body?.innerHTML?.length || 0;
                    if (Math.abs(currentState - lastState) > 100) {
                        stableTime = 0;
                        lastState = currentState;
                    } else {
                        stableTime += config.observerInterval;
                    }
                    if (stableTime >= timeout) {
                        clearInterval(interval);
                        observer.disconnect();
                        WAIT_LOGGER.debug("页面已稳定");
                        resolve(true);
                    }
                }, config.observerInterval);
                setTimeout(() => {
                    clearInterval(interval);
                    observer.disconnect();
                    if (stableTime >= timeout / 2) {
                        WAIT_LOGGER.debug("页面接近稳定,继续执行");
                        resolve(true);
                    } else {
                        WAIT_LOGGER.warn("页面稳定超时");
                        resolve(false);
                    }
                }, timeout + 1e3);
            });
        },
        async forVue(options = {}) {
            const {vueTimeout: vueTimeout = 12e3, skeletonTimeout: skeletonTimeout = 15e3, contentTimeout: contentTimeout = 15e3} = options;
            WAIT_LOGGER.debug("等待 Vue 组件就绪");
            if (window.Vue || document.querySelector("[data-v-]")?.__vue__) {
                WAIT_LOGGER.debug("检测到 Vue 实例");
                return true;
            }
            const skeletonSelectors = [ ".el-loading-mask", ".v-loading", ".ant-spin-mask", ".loading-mask", '[class*="loading"]' ];
            const startTime = Date.now();
            return new Promise(resolve => {
                const checkVueReady = () => {
                    const elapsed = Date.now() - startTime;
                    if (window.Vue || document.querySelector("[data-v-]")?.__vue__) {
                        return {
                            ready: true,
                            reason: "Vue 实例检测到"
                        };
                    }
                    const contentSelectors = [ ".el-main", ".main-content", ".content-area", ".app-main" ];
                    for (const selector of contentSelectors) {
                        const el = document.querySelector(selector);
                        if (el && el.children.length > 0) {
                            return {
                                ready: true,
                                reason: `内容区域 ${selector} 有内容`
                            };
                        }
                    }
                    let hasSkeleton = false;
                    for (const selector of skeletonSelectors) {
                        if (document.querySelector(selector)) {
                            hasSkeleton = true;
                            break;
                        }
                    }
                    if (!hasSkeleton && elapsed > skeletonTimeout) {
                        return {
                            ready: true,
                            reason: "骨架屏超时但无 Vue"
                        };
                    }
                    if (elapsed > contentTimeout) {
                        return {
                            ready: true,
                            reason: "内容加载超时,继续执行"
                        };
                    }
                    return {
                        ready: false
                    };
                };
                const interval = setInterval(() => {
                    const result = checkVueReady();
                    if (result.ready) {
                        clearInterval(interval);
                        WAIT_LOGGER.debug(`Vue 就绪: ${result.reason}`);
                        resolve(true);
                    }
                }, 200);
                setTimeout(() => {
                    clearInterval(interval);
                    const result = checkVueReady();
                    if (result.ready) {
                        resolve(true);
                    } else {
                        WAIT_LOGGER.warn("Vue 等待超时,继续执行");
                        resolve(true);
                    }
                }, Math.max(vueTimeout, skeletonTimeout, contentTimeout));
            });
        },
        async random(min = config.randomMin, max = config.randomMax) {
            const delay = Math.floor(Math.random() * (max - min + 1)) + min;
            WAIT_LOGGER.debug(`随机延时: ${delay}ms`);
            return new Promise(resolve => setTimeout(resolve, delay));
        },
        delay(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        },
        async retry(asyncFn, options = {}) {
            const {count: count = config.retryCount, delay: retryDelay = config.retryDelay, onRetry: onRetry = null} = options;
            let lastError;
            for (let i = 0; i < count; i++) {
                try {
                    return await asyncFn();
                } catch (error) {
                    lastError = error;
                    WAIT_LOGGER.warn(`重试 ${i + 1}/${count} 失败: ${error.message}`);
                    if (onRetry) {
                        await onRetry(error, i + 1);
                    }
                    if (i < count - 1) {
                        await this.delay(retryDelay);
                    }
                }
            }
            throw lastError;
        },
        async forIframe(selector, timeout = config.elementTimeout) {
            const iframe = await this.forElement(selector, timeout);
            if (!iframe) {
                return null;
            }
            if (iframe.contentDocument?.readyState === "complete") {
                return iframe;
            }
            return new Promise(resolve => {
                const onLoad = () => {
                    resolve(iframe);
                };
                iframe.addEventListener("load", onLoad);
                setTimeout(() => {
                    iframe.removeEventListener("load", onLoad);
                    WAIT_LOGGER.warn(`iframe 加载超时: ${selector}`);
                    resolve(iframe);
                }, timeout);
            });
        },
        configure(newConfig) {
            config = {
                ...config,
                ...newConfig
            };
        },
        reset() {
            config = {
                ...defaultConfig
            };
        }
    };
    const CbeadPlayerFlow = {
        async learnWithRealPlayback(course) {
            console.log(`[CbeadPlayerFlow] ========== 开始真实播放学习 ==========`);
            console.log(`[CbeadPlayerFlow] 📚 课程名称: ${course.title}`);
            console.log(`[CbeadPlayerFlow] 🆔 课程ID: ${course.id || course.courseId || "N/A"}`);
            console.log(`[CbeadPlayerFlow] 🔗 当前URL: ${window.location.href}`);
            const learningState = CbeadProgressManager.getLearningState();
            learningState.reset();
            console.log(`[CbeadPlayerFlow] 🔄 学习状态已重置`);
            const timerManager = CbeadPlayer.createIntervalManager();
            let chapterName = course.title;
            let currentChapterIndex = null;
            try {
                console.log(`[CbeadPlayerFlow] ⏳ 等待章节目录加载...`);
                const catalogSelectors = [ ".course-side-catalog", ".new-course-side-catalog", ".course-catalog" ];
                let catalogLoaded = false;
                for (const selector of catalogSelectors) {
                    const catalog = await WaitHelper.forElement(selector, 5e3);
                    if (catalog) {
                        console.log(`[CbeadPlayerFlow] ✅ 章节目录已加载: ${selector}`);
                        catalogLoaded = true;
                        break;
                    }
                }
                if (!catalogLoaded) {
                    console.warn(`[CbeadPlayerFlow] ⚠️ 章节目录未加载,尝试继续提取`);
                }
                let chapterProgress = null;
                for (let i = 0; i < 2; i++) {
                    chapterProgress = CbeadPlayer.extractChapterProgress(false);
                    if (chapterProgress && chapterProgress.firstIncomplete) {
                        break;
                    }
                    if (i < 1) {
                        console.log(`[CbeadPlayerFlow] ⏳ 第${i + 1}次提取未完成,等待2秒后重试...`);
                        await new Promise(resolve => setTimeout(resolve, 2e3));
                    }
                }
                if (chapterProgress && chapterProgress.firstIncomplete) {
                    chapterName = chapterProgress.firstIncomplete.title;
                    currentChapterIndex = chapterProgress.firstIncomplete.index;
                    console.log(`[CbeadPlayerFlow] 📖 章节名称: ${chapterName}`);
                    console.log(`[CbeadPlayerFlow] 📌 章节索引: ${currentChapterIndex}`);
                    if (currentChapterIndex > 1) {
                        console.log(`[CbeadPlayerFlow] ⏭️ 已跳过 ${currentChapterIndex - 1} 个已完成章节`);
                        EventBus.publish(CONSTANTS.EVENTS.LOG, {
                            message: `⏭️ 已跳过 ${currentChapterIndex - 1} 个已完成章节,开始学习: ${chapterName}`,
                            type: "info"
                        });
                    }
                } else {
                    console.warn(`[CbeadPlayerFlow] ⚠️ 未能提取到章节信息,使用课程标题`);
                }
            } catch (error) {
                console.warn(`[CbeadPlayerFlow] 无法提取章节信息,使用课程名:`, error);
            }
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `📖 开始学习: ${chapterName}`,
                type: "info"
            });
            console.log(`[CbeadPlayerFlow] 🔍 立即检查章节状态...`);
            const initialChapterProgress = CbeadPlayer.extractChapterProgress(false);
            if (initialChapterProgress && currentChapterIndex !== null) {
                const targetChapter = initialChapterProgress.chapters.find(ch => ch.index === currentChapterIndex);
                if (targetChapter && targetChapter.status === "completed") {
                    console.log(`[CbeadPlayerFlow] ✅ 章节 ${currentChapterIndex} 已完成,跳过播放`);
                    EventBus.publish(CONSTANTS.EVENTS.LOG, {
                        message: `⏭️ 章节已完成,跳过: ${targetChapter.title}`,
                        type: "info"
                    });
                    return {
                        success: true,
                        method: "skip_completed",
                        duration: 0,
                        watched: 0,
                        chapterName: targetChapter.title,
                        chapterCompleted: true,
                        earlyTermination: true,
                        reason: "章节已是已完成状态",
                        savedPercent: 100
                    };
                }
            }
            if (initialChapterProgress && currentChapterIndex !== null) {
                const targetChapter = initialChapterProgress.chapters.find(ch => ch.index === currentChapterIndex);
                if (targetChapter) {
                    console.log(`[CbeadPlayerFlow] 📖 目标章节: ${targetChapter.title}, 状态: ${targetChapter.status}`);
                    const currentVideo = document.querySelector("video");
                    const currentPlayer = CbeadPlayer.detectVideoPlayer();
                    if (currentPlayer && currentVideo) {
                        currentPlayer.getCurrentTime();
                        currentPlayer.getDuration();
                        const targetServerProgress = targetChapter.progress || 0;
                        if (targetServerProgress > 80 || targetServerProgress < 5) {
                            console.log(`[CbeadPlayerFlow] 🔄 服务器进度 ${targetServerProgress}%,尝试切换到目标章节: ${targetChapter.title}`);
                            const clicked = CbeadPlayer.clickChapter(targetChapter.title);
                            if (clicked) {
                                console.log(`[CbeadPlayerFlow] ✅ 已点击切换到章节: ${targetChapter.title}`);
                                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                                    message: `🔄 切换到章节: ${targetChapter.title}`,
                                    type: "info"
                                });
                                await new Promise(resolve => setTimeout(resolve, CBEAD_CONSTANTS.TIMING.CHAPTER_SWITCH_DELAY));
                            } else {
                                console.warn(`[CbeadPlayerFlow] ⚠️ 章节切换失败,可能已在正确章节`);
                            }
                        } else {
                            console.log(`[CbeadPlayerFlow] ✅ 服务器进度 ${targetServerProgress}%,似乎在正确的章节`);
                        }
                    }
                }
            }
            try {
                console.log(`[CbeadPlayerFlow] ⏳ 步骤 1/7: 等待播放器初始化...`);
                const player = await CbeadPlayer.waitForPlayerReady();
                if (!player) {
                    console.error(`[CbeadPlayerFlow] ❌ 播放器初始化失败`);
                    throw new Error("播放器初始化超时,无法获取视频信息");
                }
                console.log(`[CbeadPlayerFlow] ✅ 步骤 1/7: 播放器初始化完成`);
                console.log(`[CbeadPlayerFlow] 📹 播放器类型: ${player.type}`);
                console.log(`[CbeadPlayerFlow] ⏳ 步骤 2/7: 获取视频信息...`);
                const duration = player.getDuration();
                const currentTime = player.getCurrentTime();
                const durationMinutes = Math.round(duration / 60);
                const serverProgress = CbeadPlayer.getServerProgress(currentChapterIndex);
                console.log(`[CbeadPlayerFlow] ✅ 步骤 2/7: 视频信息获取完成`);
                console.log(`[CbeadPlayerFlow] ⏱️ 总时长: ${Math.round(duration)}秒 (${durationMinutes}分钟)`);
                console.log(`[CbeadPlayerFlow] ⏯️ 当前位置: ${Math.round(currentTime)}秒 (服务器进度: ${serverProgress !== null ? serverProgress + "%" : "N/A"})`);
                console.log(`[CbeadPlayerFlow] ⏳ 步骤 3/7: 设置静音...`);
                player.setMuted(true);
                console.log(`[CbeadPlayerFlow] 🔇 已调用 setMuted(true)`);
                const mutedAfter = player.getMuted();
                console.log(`[CbeadPlayerFlow] 🔍 静音状态验证: ${mutedAfter ? "成功" : "失败"}`);
                console.log(`[CbeadPlayerFlow] ✅ 步骤 3/7: 静音设置完成`);
                console.log(`[CbeadPlayerFlow] ⏳ 步骤 4/7: 设置播放位置...`);
                if (serverProgress > 0) {
                    const targetTime = serverProgress / 100 * duration;
                    console.log(`[CbeadPlayerFlow] 📍 策略: 恢复进度 (服务器 ${serverProgress}% → ${Math.round(targetTime)}s / ${Math.round(duration)}s)`);
                    player.setCurrentTime(targetTime);
                } else if (currentTime < CBEAD_CONSTANTS.THRESHOLDS.VIDEO_START_THRESHOLD) {
                    console.log(`[CbeadPlayerFlow] 📍 策略: 从头播放`);
                    player.setCurrentTime(0);
                } else {
                    console.log(`[CbeadPlayerFlow] 📍 策略: 断点续播 (当前位置: ${Math.round(currentTime)}s)`);
                }
                console.log(`[CbeadPlayerFlow] ✅ 步骤 4/7: 播放位置设置完成`);
                console.log(`[CbeadPlayerFlow] ⏳ 步骤 5/7: 启动播放器...`);
                const currentPlayer = CbeadPlayer.detectVideoPlayer();
                if (!currentPlayer) {
                    console.error(`[CbeadPlayerFlow] ❌ 播放器检测失败,可能已被销毁`);
                    throw new Error("播放器在启动前被销毁");
                }
                const {clickMaskButton: clickMaskButton} = await Promise.resolve().then(function() {
                    return infraDomHelper;
                });
                clickMaskButton();
                const bigPlayButton = document.querySelector(".vjs-big-play-button");
                if (bigPlayButton) {
                    console.log(`[CbeadPlayerFlow] 🎯 发现大播放按钮遮罩,准备点击...`);
                    bigPlayButton.click();
                    console.log(`[CbeadPlayerFlow] ✅ 已点击大播放按钮`);
                    await new Promise(resolve => setTimeout(resolve, 800));
                }
                if (currentPlayer.element.paused) {
                    const playControlBtn = document.querySelector(".vjs-play-control");
                    if (playControlBtn) {
                        console.log(`[CbeadPlayerFlow] 🎯 发现播放控制按钮,准备点击...`);
                        playControlBtn.click();
                        await new Promise(resolve => setTimeout(resolve, 500));
                        console.log(`[CbeadPlayerFlow] ✅ 已点击播放控制按钮`);
                    }
                }
                if (!currentPlayer.element.paused) {
                    console.log(`[CbeadPlayerFlow] ✅ 播放器成功启动`);
                } else {
                    console.error(`[CbeadPlayerFlow] ❌ 播放器启动失败`);
                    EventBus.publish(CONSTANTS.EVENTS.LOG, {
                        message: "⚠️ 自动播放失败,请手动点击播放按钮",
                        type: "warn"
                    });
                }
                console.log(`[CbeadPlayerFlow] ✅ 步骤 5/7: 播放器启动完成`);
                console.log(`[CbeadPlayerFlow] ⏳ 步骤 6/7: 设置播放监听器...`);
                let notificationPermission = "default";
                if ("Notification" in window) {
                    try {
                        notificationPermission = await Notification.requestPermission();
                    } catch (err) {
                        console.warn(`[CbeadPlayerFlow] ⚠️ 请求通知权限失败:`, err);
                    }
                }
                let wakeLock = null;
                if ("wakeLock" in navigator) {
                    try {
                        wakeLock = await navigator.wakeLock.request("screen");
                        console.log(`[CbeadPlayerFlow] 📱 已启用屏幕常亮锁`);
                        EventBus.publish(CONSTANTS.EVENTS.LOG, {
                            message: "📱 已启用屏幕常亮,防止屏幕关闭",
                            type: "info"
                        });
                    } catch (err) {
                        console.warn(`[CbeadPlayerFlow] ⚠️ 无法启用屏幕常亮锁:`, err);
                    }
                }
                const handleVisibilityChange = () => {
                    if (document.hidden) {
                        console.warn(`[CbeadPlayerFlow] ⚠️ 页面已隐藏到后台!`);
                        if (CBEAD_CONSTANTS.HEARTBEAT.ENABLED) {
                            console.log(`[CbeadPlayerFlow] 📱 页面进入后台,启动 Worker 心跳`);
                            heartbeatWorker = CbeadHeartbeatWorker.start({
                                courseId: course.id || course.courseId,
                                chapterId: currentChapterIndex,
                                interval: CBEAD_CONSTANTS.HEARTBEAT.INTERVAL
                            });
                        }
                        EventBus.publish(CONSTANTS.EVENTS.LOG, {
                            message: "⚠️ 页面已隐藏到后台,已启动Worker心跳保活",
                            type: "warn"
                        });
                        if ("Notification" in window && notificationPermission === "granted") {
                            try {
                                new Notification("学习提醒", {
                                    body: "页面已隐藏,请返回前台继续学习",
                                    icon: "📺",
                                    requireInteraction: true
                                });
                            } catch (err) {
                                console.warn(`[CbeadPlayerFlow] 发送通知失败:`, err);
                            }
                        }
                    } else {
                        console.log(`[CbeadPlayerFlow] ✅ 页面已返回前台`);
                        if (heartbeatWorker) {
                            console.log(`[CbeadPlayerFlow] 📱 页面回到前台,停止 Worker 心跳`);
                            CbeadHeartbeatWorker.stop(heartbeatWorker);
                            heartbeatWorker = null;
                        }
                        EventBus.publish(CONSTANTS.EVENTS.LOG, {
                            message: "✅ 页面已返回前台,继续学习",
                            type: "info"
                        });
                    }
                };
                document.removeEventListener("visibilitychange", handleVisibilityChange);
                document.addEventListener("visibilitychange", handleVisibilityChange);
                console.log(`[CbeadPlayerFlow] ✅ 步骤 7/7: 监听器设置完成`);
                console.log(`[CbeadPlayerFlow] ========== 开始播放,等待完成 ==========`);
                let heartbeatWorker = null;
                return new Promise((resolve, reject) => {
                    let chapterCompletedDetected = false;
                    currentPlayer.element.addEventListener("ended", () => {
                        console.log(`[CbeadPlayerFlow] ========== 播放完成 ==========`);
                        console.log(`[CbeadPlayerFlow] ✅ 视频播放完成!`);
                        if (heartbeatWorker) {
                            CbeadHeartbeatWorker.stop(heartbeatWorker);
                            heartbeatWorker = null;
                        }
                        if (wakeLock) {
                            wakeLock.release();
                            wakeLock = null;
                        }
                        document.removeEventListener("visibilitychange", handleVisibilityChange);
                        const finalWatched = Math.round(duration) - Math.round(currentTime);
                        resolve({
                            success: true,
                            method: "real_playback",
                            duration: Math.round(duration),
                            watched: finalWatched,
                            chapterName: chapterName,
                            chapterCompleted: chapterCompletedDetected,
                            earlyTermination: chapterCompletedDetected,
                            reason: chapterCompletedDetected ? "章节状态变更为已完成" : "视频播放完成"
                        });
                    }, {
                        once: true
                    });
                    currentPlayer.element.addEventListener("error", error => {
                        console.error(`[CbeadPlayerFlow] 播放错误:`, error);
                        if (heartbeatWorker) {
                            CbeadHeartbeatWorker.stop(heartbeatWorker);
                            heartbeatWorker = null;
                        }
                        if (wakeLock) wakeLock.release();
                        document.removeEventListener("visibilitychange", handleVisibilityChange);
                        reject(new Error("视频播放出错"));
                    }, {
                        once: true
                    });
                    timerManager.setInterval(() => {
                        if (learningState.isFailed()) {
                            console.warn(`[CbeadPlayerFlow] 🚨 检测到课程失败,立即停止播放`);
                            if (heartbeatWorker) {
                                CbeadHeartbeatWorker.stop(heartbeatWorker);
                                heartbeatWorker = null;
                            }
                            timerManager.clearAll();
                            if (wakeLock) wakeLock.release();
                            document.removeEventListener("visibilitychange", handleVisibilityChange);
                            currentPlayer.pause();
                            reject(new Error(`进度上报失败: ${learningState.getFailureReason()}`));
                            return;
                        }
                        const currentTime = currentPlayer.getCurrentTime();
                        const serverProgress = CbeadPlayer.getServerProgress(currentChapterIndex);
                        console.log(`[CbeadPlayerFlow] 📊 已播放: ${Math.round(currentTime)}秒 / ${Math.round(duration)}秒 (服务器进度: ${serverProgress !== null ? serverProgress + "%" : "N/A"})`);
                        if (currentPlayer.element.paused) {
                            console.warn(`[CbeadPlayerFlow] ⚠️ 检测到播放暂停,尝试恢复播放...`);
                            const playPromise = currentPlayer.play();
                            if (playPromise !== undefined) {
                                playPromise.then(() => {
                                    console.log(`[CbeadPlayerFlow] ✅ 播放已恢复`);
                                }).catch(error => {
                                    console.warn(`[CbeadPlayerFlow] ⚠️ 恢复播放失败: ${error.message}`);
                                    if (document.hidden && "Notification" in window && notificationPermission === "granted") {
                                        try {
                                            new Notification("学习暂停提醒", {
                                                body: "⚠️ 视频已暂停!请返回前台继续学习。",
                                                icon: "⚠️",
                                                requireInteraction: true
                                            });
                                        } catch (err) {
                                            console.warn(`[CbeadPlayerFlow] 发送通知失败:`, err);
                                        }
                                    }
                                });
                            }
                        }
                    }, CBEAD_CONSTANTS.TIMING.PROGRESS_REPORT_INTERVAL);
                    timerManager.setInterval(() => {
                        if (learningState.isFailed()) {
                            console.warn(`[CbeadPlayerFlow] 🚨 检测到课程失败,立即停止播放`);
                            timerManager.clearAll();
                            if (wakeLock) wakeLock.release();
                            document.removeEventListener("visibilitychange", handleVisibilityChange);
                            currentPlayer.pause();
                            reject(new Error(`进度上报失败: ${learningState.getFailureReason()}`));
                            return;
                        }
                        const serverProgress = CbeadPlayer.getServerProgress(currentChapterIndex);
                        if (serverProgress !== null && serverProgress < CBEAD_CONSTANTS.THRESHOLDS.CHAPTER_CHECK_MIN_PROGRESS) {
                            return;
                        }
                        console.log(`[CbeadPlayerFlow] 🔍 检查章节状态 (服务器进度: ${serverProgress}%)...`);
                        const chapterProgress = CbeadPlayer.extractChapterProgress(false);
                        if (chapterProgress && chapterProgress.completed === chapterProgress.total) {
                            console.log(`[CbeadPlayerFlow] 🎉 检测到所有章节已完成!`);
                            chapterCompletedDetected = true;
                            if (heartbeatWorker) {
                                CbeadHeartbeatWorker.stop(heartbeatWorker);
                                heartbeatWorker = null;
                            }
                            timerManager.clearAll();
                            if (wakeLock) wakeLock.release();
                            document.removeEventListener("visibilitychange", handleVisibilityChange);
                            currentPlayer.pause();
                            currentPlayer.setCurrentTime(duration);
                            resolve({
                                success: true,
                                method: "real_playback",
                                duration: Math.round(duration),
                                watched: Math.round(currentTime),
                                chapterName: chapterName,
                                chapterCompleted: true,
                                earlyTermination: true,
                                reason: "所有章节已完成",
                                savedPercent: 100 - (serverProgress || 0)
                            });
                        }
                        if (chapterProgress && currentChapterIndex !== null) {
                            const currentChapter = chapterProgress.chapters.find(ch => ch.index === currentChapterIndex);
                            if (currentChapter && currentChapter.status === "completed") {
                                console.log(`[CbeadPlayerFlow] 🎉 检测到章节状态变化: 章节 ${currentChapterIndex} 已完成!`);
                                chapterCompletedDetected = true;
                                if (heartbeatWorker) {
                                    CbeadHeartbeatWorker.stop(heartbeatWorker);
                                    heartbeatWorker = null;
                                }
                                timerManager.clearAll();
                                if (wakeLock) wakeLock.release();
                                document.removeEventListener("visibilitychange", handleVisibilityChange);
                                currentPlayer.pause();
                                currentPlayer.setCurrentTime(duration);
                                resolve({
                                    success: true,
                                    method: "real_playback",
                                    duration: Math.round(duration),
                                    watched: Math.round(currentTime),
                                    chapterName: chapterName,
                                    chapterCompleted: true,
                                    earlyTermination: true,
                                    reason: "章节状态变更为已完成",
                                    savedPercent: 100 - (serverProgress || 0)
                                });
                            }
                        }
                    }, CBEAD_CONSTANTS.TIMING.CHAPTER_CHECK_INTERVAL);
                });
            } catch (error) {
                timerManager.clearAll();
                console.error(`[CbeadPlayerFlow] 学习失败: ${course.title}`, error);
                throw error;
            }
        }
    };
    const CbeadHandler = {
        PAGE_TYPES: {
            PLAYER: "player",
            BRANCH_LIST: "branch_list",
            COLUMN: "column",
            HOME_V: "home_v",
            UNKNOWN: "unknown"
        },
        SELECTORS: {
            COURSE_ITEMS: [ ".activity-stage .list li", ".list-item", ".activity-list .list-item" ],
            ENTER_BTN: ".study-btn",
            PLAYER_CONTAINER: ".player-content",
            VIDEO_ELEMENT: "video",
            CHAPTER_CONTAINER: ".course-side-catalog",
            BRANCH_LIST: {
                CONTAINER: ".activity-main-area .vertical",
                ITEM: ".list-item",
                PAGINATION: ".e-pagination-box .zxy-pagination",
                NEXT_BTN: ".zxy-pagination-item-next",
                TAG_CONTAINER: ".label .tag-list",
                TAG_BTN: ".label-btn"
            }
        },
        identifyPage() {
            const url = window.location.href;
            if (url.includes(CBEAD_CONSTANTS.PATH_PATTERNS.PLAYER)) return this.PAGE_TYPES.PLAYER;
            if (url.includes(CBEAD_CONSTANTS.PATH_PATTERNS.BRANCH_LIST)) return this.PAGE_TYPES.BRANCH_LIST;
            if (url.includes(CBEAD_CONSTANTS.PATH_PATTERNS.COLUMN)) return this.PAGE_TYPES.COLUMN;
            if (url.includes(CBEAD_CONSTANTS.PATH_PATTERNS.HOME_V)) return this.PAGE_TYPES.HOME_V;
            return this.PAGE_TYPES.UNKNOWN;
        },
        isCbeadMode() {
            return CONFIG$1.CBEAD_MODE === true;
        },
        extractId() {
            const hash = window.location.hash;
            const match = hash.match(/class-detail\/([a-f0-9-]+)/);
            if (match) return match[1];
            const courseMatch = hash.match(/course\/detail\/[\d&]+\/([a-f0-9-]+)/);
            if (courseMatch) return courseMatch[1];
            return null;
        },
        getCourses() {
            return CbeadScanner.getCourses(this.SELECTORS);
        },
        scanCoursesFromColumnPage() {
            return CbeadScanner.scanCoursesFromColumnPage();
        },
        async scanCoursesFromBranchList(options = {}) {
            return await CbeadScanner.scanCoursesFromBranchList(options);
        },
        async scanCoursesFromBranchListPage() {
            return await CbeadScanner.scanCoursesFromBranchListPage();
        },
        async clickNextPage() {
            return await CbeadScanner._clickNextPage();
        },
        getTotalPages() {
            return CbeadScanner._getTotalPages();
        },
        categorizeAndSortCourses(courses) {
            return CbeadScanner.categorizeAndSortCourses(courses);
        },
        getSortedLearningList(courses) {
            return CbeadScanner.getSortedLearningList(courses);
        },
        saveLearningProgress(learningList, currentIndex) {
            return CbeadProgressManager.saveLearningProgress(learningList, currentIndex);
        },
        loadLearningProgress() {
            return CbeadProgressManager.loadLearningProgress();
        },
        clearLearningProgress() {
            return CbeadProgressManager.clearLearningProgress();
        },
        hasPendingLearningTask() {
            return CbeadProgressManager.hasPendingLearningTask();
        },
        saveLearningQueue(learningList, totalCourses, pageUrl) {
            return CbeadProgressManager.saveLearningQueue(learningList, totalCourses, pageUrl);
        },
        loadLearningQueue() {
            return CbeadProgressManager.loadLearningQueue();
        },
        updateCurrentIndex(newIndex) {
            return CbeadProgressManager.updateCurrentIndex(newIndex);
        },
        hasValidQueue(currentUrl) {
            return CbeadProgressManager.hasValidQueue(currentUrl);
        },
        returnToList(returnUrl) {
            return CbeadProgressManager.returnToList(returnUrl);
        },
        publishCompletionStats(totalCourses) {
            return CbeadProgressManager.publishCompletionStats(totalCourses);
        },
        normalizeCourseId(rawId) {
            return CbeadProgressManager.normalizeCourseId(rawId);
        },
        extractPlayerParams() {
            return CbeadProgressManager.extractPlayerParams();
        },
        detectVideoPlayer() {
            return CbeadPlayer.detectVideoPlayer();
        },
        extractChapterProgress(verbose) {
            return CbeadPlayer.extractChapterProgress(verbose);
        },
        isCourseReallyCompleted() {
            return CbeadPlayer.isCourseReallyCompleted();
        },
        extractChapterList() {
            return CbeadPlayer.extractChapterList();
        },
        extractPageCourseTitle() {
            const selectors = [ ".course-title", ".video-course-title", ".detail-title", ".study-detail-title", "h1.title", ".header-title", ".course-name", '[class*="title"]' ];
            for (const selector of selectors) {
                const el = document.querySelector(selector);
                if (el && el.textContent?.trim()) {
                    const text = el.textContent.trim();
                    if (text.length > 5 && text.length < 200 && !text.includes("中国干部网络学院")) {
                        return text;
                    }
                }
            }
            const pageText = document.body?.textContent || "";
            const courseNameMatch = pageText.match(/《([^》]+)》/);
            if (courseNameMatch && courseNameMatch[1]) {
                return courseNameMatch[1];
            }
            return null;
        },
        parseTimeToSeconds(timeStr) {
            return CbeadPlayer.parseTimeToSeconds(timeStr);
        },
        waitForPlayerReady(maxWaitTime, checkInterval) {
            return CbeadPlayer.waitForPlayerReady(maxWaitTime, checkInterval);
        },
        clickChapter(chapterTitle) {
            return CbeadPlayer.clickChapter(chapterTitle);
        },
        getServerProgress(currentChapterIndex = null) {
            return CbeadPlayer.getServerProgress(currentChapterIndex);
        },
        cleanupPlaybackResources(resources, reason) {
            return CbeadPlayer.cleanupPlaybackResources(resources, reason);
        },
        getCurrentChapterName() {
            return CbeadPlayer.getCurrentChapterName();
        },
        getLearningState() {
            return CbeadProgressManager.getLearningState();
        },
        createIntervalManager() {
            return CbeadPlayer.createIntervalManager();
        },
        async learnWithRealPlayback(course) {
            return await CbeadPlayerFlow.learnWithRealPlayback(course);
        },
        init() {
            if (!this.isCbeadMode()) {
                console.log("[CbeadHandler] 非企业分院环境,跳过初始化");
                return;
            }
            const pageType = this.identifyPage();
            console.log(`[CbeadHandler] 检测到页面类型: ${pageType}`);
            EventBus.publish("cbead:pageDetected", {
                pageType: pageType,
                url: window.location.href,
                id: this.extractId()
            });
        }
    };
    let currentPlayerLearningId = null;
    const CbeadLearner = {
        getCurrentPlayerLearningId() {
            return currentPlayerLearningId;
        },
        setCurrentPlayerLearningId(id) {
            const oldId = currentPlayerLearningId;
            currentPlayerLearningId = id;
            console.log(`[CbeadLearner] 📍 更新播放页学习任务ID: ${oldId || "none"} → ${id || "none"}`);
        },
        _validatePageType(pageType) {
            const allowedTypes = [ CbeadHandler.PAGE_TYPES.PLAYER, CbeadHandler.PAGE_TYPES.COLUMN, CbeadHandler.PAGE_TYPES.BRANCH_LIST ];
            if (allowedTypes.includes(pageType)) {
                return true;
            }
            if (pageType === CbeadHandler.PAGE_TYPES.HOME_V) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "⚠️ 当前是展示主页页面,没有可学习的课程列表。请进入课程详情页或列表页。",
                    type: "warn"
                });
                EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, "展示页无课程");
            } else {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "⚠️ 当前页面不支持自动学习。请进入专题详情页或课程列表页。",
                    type: "warn"
                });
                EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, "页面不支持");
            }
            return false;
        },
        async selectAndExecute() {
            if (!CONFIG$1.CBEAD_MODE) {
                return null;
            }
            const pageType = CbeadHandler.identifyPage();
            if (!this._validatePageType(pageType)) {
                return false;
            }
            const href = window.location.href;
            if (href.includes("study/course/detail")) {
                return await this._handlePlayerPage();
            }
            if (href.includes("train-new/class-detail")) {
                return await this._handleColumnPage();
            }
            if (href.includes("branch-list-v")) {
                return await this._handleBranchListPage();
            }
            return null;
        },
        async _handlePlayerPage() {
            return await this.startPlayerFlow();
        },
        async _handleColumnPage() {
            const {findSignUpButton: findSignUpButton, getSignUpButtonText: getSignUpButtonText} = await Promise.resolve().then(function() {
                return infraDomHelper;
            });
            const signUpButton = findSignUpButton();
            if (signUpButton) {
                const buttonText = getSignUpButtonText(signUpButton);
                const errorMsg = `🚫 该专栏/专题班未报名,无法开始自动学习。请先点击"${buttonText}"按钮完成报名。`;
                console.error(`[CbeadLearner] ${errorMsg}`);
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: errorMsg,
                    type: "error"
                });
                EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, "未报名");
                throw new Error("专栏/专题班未报名");
            }
            const sourceUrl = sessionStorage.getItem("cbeadLearnSourceUrl");
            if (!sourceUrl) {
                const currentUrl = `#${window.location.hash || ""}`;
                sessionStorage.setItem("cbeadLearnSourceUrl", currentUrl);
                console.log(`[CbeadLearner] 📌 保存来源 URL: ${currentUrl}`);
            }
            await this.startBatchFlow();
            return true;
        },
        async _handleBranchListPage() {
            const oldSavedUrl = sessionStorage.getItem("cbeadBranchListUrl");
            if (oldSavedUrl) {
                console.log(`[CbeadLearner] 🧹 清除旧的分支列表页URL保存`);
                sessionStorage.removeItem("cbeadBranchListUrl");
            }
            const sourceUrl = sessionStorage.getItem("cbeadLearnSourceUrl");
            if (!sourceUrl) {
                console.log(`[CbeadLearner] 💡 进入列表页,请点击"开始学习"按钮开始批量学习`);
                return CONSTANTS.FLOW_RESULTS.WAITING_FOR_USER;
            }
            console.log(`[CbeadLearner] 🔄 检测到批量学习流程,继续执行...`);
            await this.startBranchListFlow();
            return true;
        },
        async startPlayerFlow() {
            const playerParams = CbeadHandler.extractPlayerParams();
            const currentPageCourseId = playerParams?.uuid;
            if (currentPageCourseId && currentPlayerLearningId === currentPageCourseId) {
                console.log(`[CbeadLearner] ⏭️ 播放页学习任务已在进行中,跳过重复执行: ${currentPageCourseId.substring(0, 8)}...`);
                return false;
            }
            if (currentPageCourseId) {
                this.setCurrentPlayerLearningId(currentPageCourseId);
            }
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "🎬 检测到企业分院播放页,直接处理当前视频",
                type: "info"
            });
            const isReallyCompleted = CbeadHandler.isCourseReallyCompleted();
            if (isReallyCompleted) {
                this.setCurrentPlayerLearningId(null);
                return await this._handleAllChaptersCompleted();
            }
            return await this._learnCurrentVideo();
        },
        async _handleAllChaptersCompleted() {
            console.log("[CbeadLearner] ✅ 检测到所有章节已完成,跳过播放");
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "✅ 该课程所有章节已完成!",
                type: "success"
            });
            this._resetToggleButton("学习完成");
            return false;
        },
        async _learnCurrentVideo() {
            const playerParams = CbeadHandler.extractPlayerParams();
            if (!playerParams || !playerParams.uuid) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "❌ 无法从URL提取视频UUID",
                    type: "error"
                });
                return false;
            }
            const courseTitle = CbeadHandler.extractPageCourseTitle() || document.title;
            const currentCourse = {
                id: playerParams.uuid,
                courseId: playerParams.uuid,
                dsUnitId: playerParams.uuid,
                title: courseTitle,
                courseName: courseTitle,
                source: "cbead_current_video"
            };
            LearningState.reset();
            const success = await this._executeVideoLearning(currentCourse);
            if (success) {
                return await this._handleVideoCompleted(currentCourse);
            } else {
                await this._handleVideoFailed();
                return false;
            }
        },
        async _executeVideoLearning(course) {
            return await CbeadHandler.learnWithRealPlayback(course);
        },
        async _handleVideoCompleted(course) {
            const isReallyCompleted = CbeadHandler.isCourseReallyCompleted();
            if (!isReallyCompleted) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "✅ 当前章节学习完成,继续下一章节...",
                    type: "info"
                });
                await new Promise(resolve => setTimeout(resolve, 1e3));
                const {Learner: Learner} = await Promise.resolve().then(function() {
                    return bizLearner;
                });
                await Learner.startLearning();
                return true;
            }
            console.log("[CbeadLearner] ✅ 课程学习完成");
            this.setCurrentPlayerLearningId(null);
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "✅ 当前视频学习完成!",
                type: "success"
            });
            const returnUrl = sessionStorage.getItem("cbeadLearnSourceUrl");
            if (!returnUrl) {
                console.warn("[CbeadLearner] ⚠️ 未找到来源 URL,无法返回继续学习");
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "⚠️ 学习完成,但无法返回来源页面",
                    type: "warn"
                });
                this._resetToggleButton("学习完成");
                return false;
            }
            console.log(`[CbeadLearner] 🔄 返回来源页面: ${returnUrl}`);
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `🔄 返回列表页扫描继续...`,
                type: "info"
            });
            await new Promise(resolve => setTimeout(resolve, 2e3));
            window.location.href = returnUrl;
            this._resetToggleButton("学习完成");
            return false;
        },
        async _handleVideoFailed() {
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "❌ 视频学习失败",
                type: "error"
            });
            this._resetToggleButton("学习失败");
        },
        async startBatchFlow() {
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "📋 检测到专题班/专栏,开始批量学习流程",
                type: "info"
            });
            const allCourses = await CbeadHandler.scanCoursesFromColumnPage();
            if (allCourses.length === 0) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "❌ 未找到课程",
                    type: "error"
                });
                return false;
            }
            const learningList = CbeadHandler.getSortedLearningList(allCourses);
            if (learningList.length === 0) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "✅ 所有课程已完成!",
                    type: "success"
                });
                return false;
            }
            const totalCourses = allCourses.length;
            const skippedCount = totalCourses - learningList.length;
            EventBus.publish(CONSTANTS.EVENTS.STATISTICS_UPDATE, {
                total: totalCourses,
                completed: skippedCount,
                learned: 0,
                failed: 0,
                skipped: skippedCount
            });
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `📚 准备学习 ${learningList.length} 门课程`,
                type: "info"
            });
            const firstCourse = learningList[0];
            await this._navigateToCourse(firstCourse);
            return true;
        },
        async startBranchListFlow() {
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "📋 检测到分支列表页,开始边扫描边学习",
                type: "info"
            });
            await this._waitForPageReady();
            const scanPage = this._loadScanPage();
            let currentPage = scanPage;
            console.log(`[CbeadLearner] 📄 当前扫描页码: ${currentPage}`);
            const result = await this._scanAndLearnBranchList(currentPage);
            if (result && result.completed) {
                this._clearScanPage();
                try {
                    sessionStorage.removeItem("cbeadLearnSourceUrl");
                    console.log(`[CbeadLearner] 🧹 清除来源 URL`);
                } catch (e) {
                    console.warn("[CbeadLearner] 清除来源 URL 失败:", e);
                }
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "✅ 所有课程已完成!",
                    type: "success"
                });
                this._resetToggleButton("学习完成");
            }
            return true;
        },
        async _scanAndLearnBranchList(startPage) {
            const pageCourses = await CbeadScanner.scanCoursesFromBranchListPage();
            if (!pageCourses || pageCourses.length === 0) {
                console.log("[CbeadLearner] 当前页没有找到课程");
                const hasNext = await CbeadScanner._clickNextPage();
                if (hasNext) {
                    return {
                        continue: true,
                        nextPage: startPage + 1
                    };
                } else {
                    return {
                        completed: true
                    };
                }
            }
            const incompleteCourses = pageCourses.filter(c => c.progress < 100);
            if (incompleteCourses.length > 0) {
                const currentUrl = `#${window.location.hash || ""}`;
                sessionStorage.setItem("cbeadLearnSourceUrl", currentUrl);
                console.log(`[CbeadLearner] 📌 保存来源 URL: ${currentUrl}`);
                await this._navigateToCourse(incompleteCourses[0]);
                return {
                    continue: true,
                    nextPage: startPage
                };
            }
            const hasNext = await CbeadScanner._clickNextPage();
            if (hasNext) {
                const loaded = await this._waitForPageLoad();
                if (loaded) {
                    return await this._scanAndLearnBranchList(startPage + 1);
                }
                return {
                    continue: true,
                    nextPage: startPage + 1
                };
            } else {
                return {
                    completed: true
                };
            }
        },
        _loadScanPage() {
            try {
                const data = sessionStorage.getItem("cbeadScanPage");
                if (data) {
                    return parseInt(data, 10);
                }
            } catch (e) {
                console.warn("[CbeadLearner] 读取扫描页码失败:", e);
            }
            return 1;
        },
        _saveScanPage(page) {
            try {
                sessionStorage.setItem("cbeadScanPage", page.toString());
            } catch (e) {
                console.warn("[CbeadLearner] 保存扫描页码失败:", e);
            }
        },
        _clearScanPage() {
            try {
                sessionStorage.removeItem("cbeadScanPage");
            } catch (e) {
                console.warn("[CbeadLearner] 清除扫描页码失败:", e);
            }
        },
        _clearBranchListUrl() {
            try {
                sessionStorage.removeItem("cbeadBranchListUrl");
            } catch (e) {
                console.warn("[CbeadLearner] 清除分支列表页URL失败:", e);
            }
        },
        async _waitForPageLoad() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const timeout = CBEAD_CONSTANTS.TIMEOUT?.PAGE_LOAD;
            console.log("[CbeadLearner] ⏳ 等待翻页加载完成...");
            const startTime = Date.now();
            while (Date.now() - startTime < timeout) {
                const container = document.querySelector(config.CONTAINER_SELECTOR);
                if (container) {
                    const items = container.querySelectorAll(config.ITEM_SELECTOR);
                    if (items.length > 0) {
                        console.log(`[CbeadLearner] ✅ 翻页加载完成,找到 ${items.length} 个课程项`);
                        return true;
                    }
                }
                await new Promise(resolve => setTimeout(resolve, 500));
            }
            console.warn("[CbeadLearner] ⚠️ 翻页加载超时,继续执行");
            return false;
        },
        async _waitForPageReady() {
            const config = CBEAD_CONSTANTS.BRANCH_LIST;
            const timeout = CBEAD_CONSTANTS.TIMEOUT?.PAGE_LOAD;
            console.log("[CbeadLearner] ⏳ 等待页面渲染完成...");
            const startTime = Date.now();
            while (Date.now() - startTime < timeout) {
                const container = document.querySelector(config.CONTAINER_SELECTOR);
                if (container) {
                    const items = container.querySelectorAll(config.ITEM_SELECTOR);
                    if (items.length > 0) {
                        console.log(`[CbeadLearner] ✅ 页面渲染完成,找到 ${items.length} 个课程项`);
                        return true;
                    }
                }
                await new Promise(resolve => setTimeout(resolve, 500));
            }
            console.warn("[CbeadLearner] ⚠️ 等待页面渲染超时,继续执行");
            return false;
        },
        async selectCourse(options = {}) {
            const {scanMethod: scanMethod} = options;
            if (!scanMethod) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "❌ 未提供扫描方法",
                    type: "error"
                });
                return false;
            }
            const allCourses = await scanMethod();
            if (allCourses.length === 0) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "❌ 未找到课程",
                    type: "error"
                });
                return false;
            }
            const learningList = CbeadHandler.getSortedLearningList(allCourses);
            if (learningList.length === 0) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "✅ 所有课程已完成!",
                    type: "success"
                });
                return false;
            }
            const totalCourses = allCourses.length;
            const skippedCount = totalCourses - learningList.length;
            EventBus.publish(CONSTANTS.EVENTS.STATISTICS_UPDATE, {
                total: totalCourses,
                completed: skippedCount,
                learned: 0,
                failed: 0,
                skipped: skippedCount
            });
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `📚 准备学习 ${learningList.length} 门课程`,
                type: "info"
            });
            const firstCourse = learningList[0];
            await this._navigateToCourse(firstCourse);
            return true;
        },
        async _navigateToCourse(course) {
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `\n📖 开始学习: ${course.title} (${course.progress}%)`,
                type: "info"
            });
            const currentUrl = window.location.href;
            const sourceUrl = `#${currentUrl.split("#")[1] || ""}`;
            sessionStorage.setItem("cbeadLearnSourceUrl", sourceUrl);
            console.log(`[CbeadLearner] 📌 保存来源 URL: ${sourceUrl}`);
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `📌 保存来源页: ${sourceUrl}`,
                type: "info"
            });
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `🔄 正在跳转到播放页...`,
                type: "info"
            });
            await new Promise(resolve => setTimeout(resolve, 1e3));
            window.location.href = course.link;
        },
        _resetToggleButton(statusText) {
            const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace("#", ""));
            if (toggleBtn) {
                toggleBtn.setAttribute("data-state", "stopped");
                toggleBtn.textContent = "开始学习";
            }
            EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, statusText);
        }
    };
    var learner = Object.freeze({
        __proto__: null,
        CbeadLearner: CbeadLearner,
        default: CbeadLearner
    });
    const ENVIRONMENT_IDS = {
        PUDONG: "pudong",
        CBEAD: "cbead"
    };
    const ApiFactory = {
        _instances: {
            [ENVIRONMENT_IDS.PUDONG]: null,
            [ENVIRONMENT_IDS.CBEAD]: null
        },
        createApi(envId) {
            if (this._instances[envId]) {
                return this._instances[envId];
            }
            let apiInstance;
            switch (envId) {
              case ENVIRONMENT_IDS.PUDONG:
                apiInstance = PudongApi;
                break;

              case ENVIRONMENT_IDS.CBEAD:
                apiInstance = CbeadApi;
                break;

              default:
                throw new Error(`未知的 API 类型: ${envId}`);
            }
            this._instances[envId] = apiInstance;
            return apiInstance;
        },
        getPudongApi() {
            return this.createApi(ENVIRONMENT_IDS.PUDONG);
        },
        getCbeadApi() {
            return this.createApi(ENVIRONMENT_IDS.CBEAD);
        },
        getCurrentApi() {
            const config = ServiceLocator.get(ServiceNames.CONFIG);
            if (config?.PUDONG_MODE) {
                return this.getPudongApi();
            }
            if (config?.CBEAD_MODE) {
                return this.getCbeadApi();
            }
            return this.getPudongApi();
        },
        getCurrentConfig() {
            const config = ServiceLocator.get(ServiceNames.CONFIG);
            if (config?.PUDONG_MODE) {
                return PUDONG_API_CONFIG;
            }
            if (config?.CBEAD_MODE) {
                return CBEAD_API_CONFIG;
            }
            return PUDONG_API_CONFIG;
        },
        clearCache() {
            this._instances = {
                [ENVIRONMENT_IDS.PUDONG]: null,
                [ENVIRONMENT_IDS.CBEAD]: null
            };
        }
    };
    const API$1 = Object.assign({}, PudongApi, CbeadApi, PUDONG_API_CONFIG, CBEAD_API_CONFIG, {
        factory: ApiFactory
    });
    const UI_CSS_CONTENT = '#api-learner-panel { all: initial !important; position: fixed !important; bottom: 20px !important; right: 20px !important; left: auto !important; top: auto !important; width: 400px !important; height: auto !important; min-height: 200px !important; margin: 0 !important; padding: 0 !important; transform: none !important; zoom: 1 !important; background: #ffffff !important; border: 1px solid #dddddd !important; border-radius: 8px !important; box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important; z-index: 2147483647 !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important; font-size: 14px !important; color: #333333 !important; line-height: 1.5 !important; text-align: left !important; box-sizing: border-box !important; display: flex !important; flex-direction: column !important; overflow: hidden !important; } #api-learner-panel * { all: unset !important; box-sizing: border-box !important; font-family: inherit !important; background: transparent !important; margin: 0 !important; padding: 0 !important; border: none !important; } #api-learner-panel *:before, #api-learner-panel *:after { content: none !important; display: none !important; } #api-learner-panel .header { display: block !important; background: #f7f7f7 !important; padding: 10px 15px !important; font-weight: bold !important; border-bottom: 1px solid #ddd !important; width: 100% !important; } #api-learner-panel .content { display: block !important; padding: 15px !important; width: 100% !important; background: #ffffff !important; flex-grow: 1 !important; } #api-learner-panel .status { display: block !important; margin-bottom: 10px !important; font-weight: bold !important; } #api-learner-panel .statistics { display: flex !important; justify-content: space-between !important; margin-bottom: 10px !important; padding: 8px !important; background: #f9f9f9 !important; border-radius: 4px !important; font-size: 12px !important; width: 100% !important; } #api-learner-panel .stat-item { display: block !important; text-align: center !important; flex: 1 !important; } #api-learner-panel .progress-bar { display: block !important; height: 8px !important; background: #eeeeee !important; border-radius: 4px !important; overflow: hidden !important; margin-bottom: 10px !important; width: 100% !important; } #api-learner-panel #learner-progress-inner { display: block !important; height: 100% !important; width: 0% !important; background: #4caf50 !important; transition: width 0.3s ease !important; } #api-learner-panel .log-container { display: block !important; height: 150px !important; overflow-y: auto !important; background: #fafafa !important; padding: 8px !important; border: 1px solid #eeeeee !important; border-radius: 4px !important; font-size: 11px !important; line-height: 1.4 !important; font-family: monospace !important; width: 100% !important; } #api-learner-panel .log-entry { display: block !important; margin-bottom: 4px !important; border-left: 2px solid #ccc !important; padding-left: 6px !important; word-break: break-all !important; } #api-learner-panel .log-entry.error { color: #f44336 !important; border-left-color: #f44336 !important; } #api-learner-panel .log-entry.success { color: #4caf50 !important; border-left-color: #4caf50 !important; } #api-learner-panel .log-entry.warn { color: #ff9800 !important; border-left-color: #ff9800 !important; } #api-learner-panel .log-entry.info { color: #2196f3 !important; border-left-color: #2196f3 !important; } #api-learner-panel .footer { display: block !important; padding: 10px 15px !important; border-top: 1px solid #dddddd !important; text-align: center !important; width: 100% !important; background: #ffffff !important; } #api-learner-panel button { display: inline-block !important; padding: 8px 16px !important; border-radius: 4px !important; cursor: pointer !important; font-size: 13px !important; font-weight: bold !important; line-height: 1.2 !important; background-color: #2196f3 !important; color: #ffffff !important; margin-left: 8px !important; vertical-align: middle !important; } #api-learner-panel button#toggle-learning-btn[data-state="running"] { background-color: #f44336 !important; } #api-learner-panel .incompatible-banner { display: flex !important; align-items: center !important; padding: 12px 15px !important; background: #fff9e6 !important; border-bottom: 1px solid #ffe082 !important; } #api-learner-panel .incompatible-banner .warning-icon { font-size: 24px !important; margin-right: 12px !important; opacity: 0.8 !important; } #api-learner-panel .incompatible-banner .warning-content { flex: 1 !important; } #api-learner-panel .incompatible-banner .warning-title { font-size: 14px !important; font-weight: bold !important; color: #f57c00 !important; margin-bottom: 3px !important; } #api-learner-panel .incompatible-banner .warning-message { font-size: 12px !important; color: #f57c00 !important; line-height: 1.4 !important; opacity: 0.85 !important; } #api-learner-panel.incompatible-mode { border: 2px solid #ffe082 !important; box-shadow: 0 2px 8px rgba(245, 124, 0, 0.15) !important; } #api-learner-panel.incompatible-mode .header { background: #fffde7 !important; color: #f57c00 !important; }';
    const UI = {
        logs: [],
        logBuffer: [],
        logUpdateTimeout: null,
        statistics: {
            total: 0,
            completed: 0,
            learned: 0,
            failed: 0,
            skipped: 0
        },
        createPanel: () => {
            const panel = document.createElement("div");
            panel.id = "api-learner-panel";
            panel.innerHTML = `\n            <div class="header">\n                cela学习助手 v4.0.0\n            </div>\n            <div class="content">\n                <div class="status">状态: <span id="learner-status">待命</span></div>\n                <div class="statistics">\n                    <div class="stat-item">总计: <span id="stat-total">0</span></div>\n                    <div class="stat-item">已完成: <span id="stat-completed">0</span></div>\n                    <div class="stat-item">新学习: <span id="stat-learned">0</span></div>\n                    <div class="stat-item">失败: <span id="stat-failed">0</span></div>\n                    <div class="stat-item">跳过: <span id="stat-skipped">0</span></div>\n                </div>\n                <div class="progress-bar"><div id="learner-progress-inner"></div></div>\n\n                <div class="log-container"></div>\n            </div>\n            <div class="footer">\n                <button id="toggle-learning-btn" data-state="stopped">开始学习</button>\n            </div>\n        `;
            document.body.appendChild(panel);
            UI.addStyles();
            UI.initEventListeners();
        },
        log: function(message, type = "info") {
            const timestamp = (new Date).toLocaleTimeString();
            const logMessage = `[${timestamp}] ${message}`;
            this.logBuffer.push({
                message: logMessage,
                type: type
            });
            if (this.logUpdateTimeout) clearTimeout(this.logUpdateTimeout);
            this.logUpdateTimeout = setTimeout(() => this.flushLogBuffer(), CONSTANTS.UI_LIMITS.LOG_FLUSH_DELAY);
            if (typeof CONFIG !== "undefined" && CONFIG.DEBUG_MODE) {
                const debugMessage = `[API Learner Debug] ${logMessage}`;
                console.log(debugMessage);
                this.logs.push(debugMessage);
            }
        },
        initEventListeners: function() {
            EventBus.subscribe(CONSTANTS.EVENTS.LOG, ({message: message, type: type}) => this.log(message, type));
            EventBus.subscribe(CONSTANTS.EVENTS.STATUS_UPDATE, status => this.updateStatus(status));
            EventBus.subscribe(CONSTANTS.EVENTS.PROGRESS_UPDATE, progress => this.updateProgress(progress));
            EventBus.subscribe(CONSTANTS.EVENTS.STATISTICS_UPDATE, stats => this.updateStatistics(stats));
            EventBus.subscribe(CONSTANTS.EVENTS.LEARNING_START, () => {
                const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace("#", ""));
                if (toggleBtn) {
                    toggleBtn.setAttribute("data-state", "running");
                    toggleBtn.textContent = "停止学习";
                }
            });
            EventBus.subscribe(CONSTANTS.EVENTS.LEARNING_STOP, () => {
                const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace("#", ""));
                if (toggleBtn) {
                    toggleBtn.setAttribute("data-state", "stopped");
                    toggleBtn.textContent = "开始学习";
                }
            });
            EventBus.subscribe(CONSTANTS.EVENTS.COURSE_START, ({course: course, index: index, total: total}) => {
                this.log(`\n📚 处理第 ${index}/${total} 门课程: ${course.title}`);
            });
            EventBus.subscribe(CONSTANTS.EVENTS.COURSE_COMPLETE, ({course: course}) => {
                this.log(`✅ 课程学习完成: ${course.title}`, "success");
            });
            EventBus.subscribe(CONSTANTS.EVENTS.COURSE_SKIP, ({course: course, reason: reason}) => {
                this.log(`✅ 课程已完成,跳过: ${course.title} (${reason})`, "success");
            });
            EventBus.subscribe(CONSTANTS.EVENTS.COURSE_ERROR, ({course: course, reason: reason}) => {
                this.log(`❌ 课程处理失败: ${course.title} - ${reason}`, "error");
            });
            EventBus.subscribe(CONSTANTS.EVENTS.PROGRESS_REPORT, _data => {});
            EventBus.subscribe(CONSTANTS.EVENTS.PROGRESS_SUCCESS, ({message: message, _progress: _progress}) => {
                this.log(message, "success");
            });
            EventBus.subscribe(CONSTANTS.EVENTS.PROGRESS_ERROR, ({message: message}) => {
                this.log(message, "warn");
            });
        },
        flushLogBuffer: function() {
            const logContainer = document.querySelector(CONSTANTS.SELECTORS.LOG_CONTAINER);
            if (!logContainer || this.logBuffer.length === 0) return;
            const fragment = document.createDocumentFragment();
            this.logBuffer.forEach(log => {
                const logEntry = document.createElement("div");
                logEntry.className = `log-entry ${log.type}`;
                logEntry.textContent = log.message;
                fragment.appendChild(logEntry);
            });
            logContainer.appendChild(fragment);
            logContainer.scrollTop = logContainer.scrollHeight;
            const entries = logContainer.querySelectorAll(".log-entry");
            if (entries.length > CONSTANTS.UI_LIMITS.MAX_LOG_ENTRIES) {
                for (let i = 0; i < entries.length - CONSTANTS.UI_LIMITS.MAX_LOG_ENTRIES; i++) {
                    entries[i].remove();
                }
            }
            this.logBuffer = [];
        },
        updateStatus: status => {
            const statusEl = document.getElementById(CONSTANTS.SELECTORS.STATUS_DISPLAY.replace("#", ""));
            if (statusEl) statusEl.textContent = status;
        },
        updateProgress: percentage => {
            const progressInner = document.getElementById(CONSTANTS.SELECTORS.PROGRESS_INNER.replace("#", ""));
            if (progressInner) progressInner.style.width = `${percentage}%`;
        },
        updateStatistics: stats => {
            Object.assign(UI.statistics, stats);
            const totalEl = document.getElementById(CONSTANTS.SELECTORS.STAT_TOTAL.replace("#", ""));
            const completedEl = document.getElementById(CONSTANTS.SELECTORS.STAT_COMPLETED.replace("#", ""));
            const learnedEl = document.getElementById(CONSTANTS.SELECTORS.STAT_LEARNED.replace("#", ""));
            const failedEl = document.getElementById(CONSTANTS.SELECTORS.STAT_FAILED.replace("#", ""));
            const skippedEl = document.getElementById(CONSTANTS.SELECTORS.STAT_SKIPPED.replace("#", ""));
            if (totalEl) totalEl.textContent = UI.statistics.total;
            if (completedEl) completedEl.textContent = UI.statistics.completed;
            if (learnedEl) learnedEl.textContent = UI.statistics.learned;
            if (failedEl) failedEl.textContent = UI.statistics.failed;
            if (skippedEl) skippedEl.textContent = UI.statistics.skipped;
        },
        addStyles: () => {
            {
                const styleSheet = document.createElement("style");
                styleSheet.type = "text/css";
                styleSheet.textContent = UI_CSS_CONTENT;
                document.head.appendChild(styleSheet);
            }
        },
        setIncompatible: reason => {
            UI.updateStatus("⚠️ 当前页面暂不兼容");
            UI.log(`[兼容性检查] ${reason}`, "warn");
            const panel = document.getElementById("api-learner-panel");
            if (!panel) return;
            panel.classList.add("incompatible-mode");
            if (panel.querySelector(".incompatible-banner")) return;
            const warningBanner = document.createElement("div");
            warningBanner.className = "incompatible-banner";
            warningBanner.innerHTML = `\n            <div class="warning-icon">⚠️</div>\n            <div class="warning-content">\n                <div class="warning-title">当前环境暂不支持</div>\n                <div class="warning-message">${reason}</div>\n            </div>\n        `;
            const header = panel.querySelector(".header");
            if (header) {
                panel.insertBefore(warningBanner, header);
            }
        },
        exportLogs: () => {
            if (UI.logs.length === 0) {
                alert("没有可导出的调试日志。");
                return;
            }
            const blob = new Blob([ UI.logs.join("\r\n") ], {
                type: "text/plain;charset=utf-8"
            });
            const url = URL.createObjectURL(blob);
            const a = document.createElement("a");
            a.href = url;
            a.download = `api_learner_debug_log_${(new Date).toISOString().slice(0, 19).replace(/:/g, "-")}.txt`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }
    };
    const SM_LOGGER = createLogger("PudongStateManager");
    CONSTANTS.LEARNING_STATES;
    const PudongStateManager = {
        status: CONSTANTS.LEARNING_STATES.IDLE,
        currentCourse: null,
        progress: {
            currentPercent: 0,
            watchedSeconds: 0,
            totalSeconds: 0,
            currentChapter: 0,
            totalChapters: 0
        },
        failureReason: null,
        async startCourse(course) {
            this.currentCourse = course;
            this.status = CONSTANTS.LEARNING_STATES.PREPARING;
            this.failureReason = null;
            this.progress = {
                currentPercent: 0,
                watchedSeconds: 0,
                totalSeconds: 0,
                currentChapter: 0,
                totalChapters: 0
            };
            EventBus.publish(CONSTANTS.EVENTS.COURSE_START, {
                course: course,
                status: this.status
            });
            SM_LOGGER.info(`开始课程: ${course.title}`);
        },
        async updateProgress(updates) {
            this.progress = {
                ...this.progress,
                ...updates
            };
            EventBus.publish(CONSTANTS.EVENTS.PROGRESS_UPDATE, {
                percent: this.progress.currentPercent,
                watched: this.progress.watchedSeconds,
                total: this.progress.totalSeconds
            });
        },
        async setLearning() {
            this.status = CONSTANTS.LEARNING_STATES.LEARNING;
            SM_LOGGER.debug("进入学习状态");
        },
        async complete(result = {}) {
            this.status = CONSTANTS.LEARNING_STATES.COMPLETED;
            EventBus.publish(CONSTANTS.EVENTS.COURSE_COMPLETE, {
                course: this.currentCourse,
                result: result
            });
            SM_LOGGER.info(`课程完成: ${this.currentCourse?.title}`, result);
        },
        async skip(reason) {
            this.status = CONSTANTS.LEARNING_STATES.SKIPPED;
            EventBus.publish(CONSTANTS.EVENTS.COURSE_SKIP, {
                course: this.currentCourse,
                reason: reason
            });
            SM_LOGGER.info(`课程跳过: ${this.currentCourse?.title}, 原因: ${reason}`);
        },
        async fail(reason, error = null) {
            this.status = CONSTANTS.LEARNING_STATES.FAILED;
            this.failureReason = reason;
            EventBus.publish(CONSTANTS.EVENTS.COURSE_ERROR, {
                course: this.currentCourse,
                reason: reason,
                error: error
            });
            SM_LOGGER.error(`课程失败: ${this.currentCourse?.title}, 原因: ${reason}`, error);
        },
        async reset() {
            this.status = CONSTANTS.LEARNING_STATES.IDLE;
            this.currentCourse = null;
            this.failureReason = null;
            this.progress = {
                currentPercent: 0,
                watchedSeconds: 0,
                totalSeconds: 0,
                currentChapter: 0,
                totalChapters: 0
            };
            SM_LOGGER.info("状态已重置");
        },
        isLearning() {
            return this.status === CONSTANTS.LEARNING_STATES.LEARNING;
        },
        isCompleted() {
            return this.status === CONSTANTS.LEARNING_STATES.COMPLETED;
        },
        getState() {
            return {
                status: this.status,
                course: this.currentCourse,
                progress: this.progress,
                failureReason: this.failureReason
            };
        }
    };
    const PROC_LOGGER = createLogger("PudongProcessor");
    const PudongProcessor = {
        async processCourse(course, options = {}) {
            const {skipCompleted: skipCompleted = true} = options;
            const courseId = course.id || course.courseId;
            course.dsUnitId;
            PROC_LOGGER.info(`开始处理课程: ${course.title}`, {
                courseId: courseId
            });
            EventBus.publish(CONSTANTS.EVENTS.COURSE_START, {
                course: course,
                courseId: courseId
            });
            try {
                const prepResult = await this.prepare(course, {
                    skipCompleted: skipCompleted
                });
                if (prepResult.action === "skip") {
                    PROC_LOGGER.info(`课程跳过: ${course.title}, 原因: ${prepResult.reason}`);
                    return {
                        action: "skip",
                        course: course,
                        reason: prepResult.reason
                    };
                }
                if (prepResult.action === "fail") {
                    PROC_LOGGER.warn(`课程准备失败: ${course.title}`);
                    return {
                        action: "fail",
                        course: course,
                        reason: prepResult.reason
                    };
                }
                const execResult = await this.execute(course, prepResult.playInfo);
                if (!execResult.success) {
                    await PudongStateManager.fail(execResult.reason || "学习执行失败");
                    return {
                        action: "fail",
                        course: course,
                        reason: execResult.reason
                    };
                }
                await this.cleanup();
                await PudongStateManager.complete(execResult);
                PROC_LOGGER.info(`课程完成: ${course.title}`, execResult);
                return {
                    action: "complete",
                    course: course,
                    result: execResult
                };
            } catch (error) {
                PROC_LOGGER.error(`课程处理异常: ${course.title}`, error);
                await PudongStateManager.fail("unknown_error", error);
                return {
                    action: "fail",
                    course: course,
                    reason: error.message
                };
            }
        },
        async prepare(course, options = {}) {
            const {skipCompleted: skipCompleted = true} = options;
            const courseId = course.id || course.courseId;
            const coursewareId = course.dsUnitId;
            await PudongStateManager.startCourse(course);
            if (skipCompleted && CONSTANTS.SKIP_COMPLETED_COURSES) {
                try {
                    const completionCheck = await PudongApi.checkCompletion(courseId, coursewareId);
                    if (completionCheck.isCompleted) {
                        await PudongStateManager.skip(`已完成 (${completionCheck.finishedRate}%)`);
                        return {
                            action: "skip",
                            reason: "课程已完成"
                        };
                    }
                } catch (e) {
                    PROC_LOGGER.warn("完成度检查失败,继续学习", e);
                }
            }
            let playInfo;
            try {
                playInfo = await PudongApi.getPlayInfo(courseId, course.dsUnitId, course.durationStr);
            } catch (e) {
                PROC_LOGGER.warn("获取播放信息失败,使用默认值", e);
                playInfo = {
                    courseId: courseId,
                    coursewareId: coursewareId || courseId,
                    videoId: `mock_${courseId}`,
                    duration: CONSTANTS.TIME_FORMATS.DEFAULT_DURATION,
                    lastLearnedTime: 0
                };
            }
            if (!playInfo) {
                await PudongStateManager.fail("无法获取播放信息");
                return {
                    action: "fail",
                    reason: "无法获取课程播放信息"
                };
            }
            await PudongStateManager.updateProgress({
                totalSeconds: playInfo.duration,
                watchedSeconds: playInfo.lastLearnedTime
            });
            const progressPercent = Math.floor(playInfo.lastLearnedTime / playInfo.duration * 100);
            if (progressPercent >= CONSTANTS.COMPLETION_THRESHOLD) {
                await PudongStateManager.skip(`进度已达 ${progressPercent}%`);
                return {
                    action: "skip",
                    reason: "播放信息确认已完成"
                };
            }
            PROC_LOGGER.info(`课程准备完成: ${course.title}`, {
                progress: `${progressPercent}%`,
                duration: playInfo.duration
            });
            return {
                action: "learn",
                playInfo: playInfo
            };
        },
        async execute(course, playInfo) {
            await PudongStateManager.setLearning();
            try {
                const courseInfo = {
                    ...course,
                    ...playInfo,
                    title: course.title || course.courseName,
                    courseId: course.id || course.courseId
                };
                const success = await PudongApi.reportProgress(playInfo, playInfo.duration - 30);
                if (success) {
                    return {
                        success: true,
                        reason: "策略执行成功"
                    };
                } else {
                    return {
                        success: false,
                        reason: "策略执行失败"
                    };
                }
            } catch (error) {
                return {
                    success: false,
                    reason: error.message
                };
            }
        },
        async cleanup() {
            PROC_LOGGER.debug("课后清理完成");
        },
        async coolingDown(isLast, stopChecker = null) {
            if (isLast) return;
            const {CONFIG: CONFIG} = await Promise.resolve().then(function() {
                return infraConfig;
            });
            const minDelay = CONFIG.COOLING_MIN_DELAY || 5e3;
            const maxDelay = CONFIG.COOLING_MAX_DELAY || 1e4;
            const delay = Math.random() * (maxDelay - minDelay) + minDelay;
            const seconds = Math.round(delay / 1e3);
            PROC_LOGGER.info(`冷却等待: ${seconds}秒`);
            for (let i = seconds; i > 0; i--) {
                if (stopChecker && stopChecker()) {
                    PROC_LOGGER.info("冷却被中断");
                    return;
                }
                EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, `等待中 (${i}s)`);
                await new Promise(r => setTimeout(r, 1e3));
            }
        }
    };
    const PudongLearner = {
        PAGE_TYPES: {
            PLAYER: "player",
            COLUMN: "column",
            INDEX: "index",
            UNKNOWN: "unknown"
        },
        identifyPage() {
            return PudongHandler.identifyPage();
        },
        isPudongMode() {
            return PudongHandler.isPudongMode();
        },
        async selectAndExecute() {
            if (!this.isPudongMode()) {
                return null;
            }
            const pageType = this.identifyPage();
            if (pageType === this.PAGE_TYPES.PLAYER) {
                return await this._handlePlayerPage();
            }
            if (pageType === this.PAGE_TYPES.COLUMN) {
                return await this._handleColumnPage();
            }
            if (pageType === this.PAGE_TYPES.INDEX) {
                return await this._handleIndexPage();
            }
            return null;
        },
        async _handlePlayerPage() {
            console.log("[PudongLearner] 处理浦东播放页");
            try {
                const result = await PudongHandler.startPlayerFlow();
                if (result) {
                    EventBus.publish(CONSTANTS.EVENTS.LEARNING_START, {
                        source: "pudong_player"
                    });
                    return true;
                }
                return false;
            } catch (error) {
                console.error("[PudongLearner] 播放页处理失败:", error);
                return false;
            }
        },
        async _handleColumnPage() {
            console.log("[PudongLearner] 处理浦东专栏页");
            try {
                const courses = await PudongHandler.scanCourses();
                if (courses && courses.length > 0) {
                    console.log(`[PudongLearner] 扫描到 ${courses.length} 门课程`);
                    return false;
                }
                return false;
            } catch (error) {
                console.error("[PudongLearner] 专栏页处理失败:", error);
                return false;
            }
        },
        async _handleIndexPage() {
            console.log("[PudongLearner] 处理浦东首页");
            try {
                const courses = await PudongHandler.scanCourses();
                console.log(`[PudongLearner] 首页扫描到 ${courses?.length || 0} 门课程`);
                return false;
            } catch (error) {
                console.error("[PudongLearner] 首页处理失败:", error);
                return false;
            }
        },
        async processCourses(courses, options = {}) {
            const results = {
                total: courses.length,
                completed: 0,
                skipped: 0,
                failed: 0,
                details: []
            };
            const {stopChecker: stopChecker = null} = options;
            for (let i = 0; i < courses.length; i++) {
                if (stopChecker && stopChecker()) {
                    console.log("[PudongLearner] 用户停止学习");
                    break;
                }
                const course = courses[i];
                const isLast = i === courses.length - 1;
                try {
                    const result = await PudongProcessor.processCourse(course, {
                        skipCompleted: true
                    });
                    if (result.action === "complete") {
                        results.completed++;
                    } else if (result.action === "skip") {
                        results.skipped++;
                    } else {
                        results.failed++;
                    }
                    results.details.push({
                        courseId: course.id || course.courseId,
                        title: course.title || course.courseName,
                        action: result.action,
                        reason: result.reason
                    });
                    EventBus.publish(CONSTANTS.EVENTS.STATISTICS_UPDATE, results);
                    if (!isLast && result.action !== "fail") {
                        await PudongProcessor.coolingDown(isLast, stopChecker);
                    }
                } catch (error) {
                    console.error(`[PudongLearner] 处理课程失败: ${course.title}`, error);
                    results.failed++;
                    results.details.push({
                        courseId: course.id || course.courseId,
                        title: course.title || course.courseName,
                        action: "error",
                        reason: error.message
                    });
                }
            }
            console.log("[PudongLearner] 批量处理完成", results);
            return results;
        },
        async getPlayerCoursewareList() {
            const courseId = this._extractCourseIdFromUrl();
            if (!courseId) {
                console.warn("[PudongLearner] 无法提取课程ID");
                return [];
            }
            return await PudongApi.getCoursewareList(courseId);
        },
        _extractCourseIdFromUrl() {
            const url = new URL(window.location.href);
            return url.searchParams.get("courseId") || url.searchParams.get("id");
        },
        async reset() {
            await PudongStateManager.reset();
        },
        getState() {
            return PudongStateManager.getState();
        }
    };
    const FlowOrchestrator = {
        async selectAndExecute() {
            if (CONFIG$1.CBEAD_MODE) {
                const cbeadResult = await CbeadLearner.selectAndExecute();
                if (cbeadResult !== null) {
                    return cbeadResult;
                }
            }
            if (CONFIG$1.PUDONG_MODE) {
                const pudongResult = await PudongLearner.selectAndExecute();
                if (pudongResult !== null) {
                    return pudongResult;
                }
            }
            return null;
        },
        getCurrentApi() {
            return ApiFactory.getCurrentApi();
        },
        getPudongApi() {
            return ApiFactory.getPudongApi();
        },
        getCbeadApi() {
            return ApiFactory.getCbeadApi();
        }
    };
    let API = null;
    function setAPI(api) {
        API = api;
    }
    const Learner = {
        state: CONSTANTS.LEARNING_STATES.IDLE,
        isRunning: false,
        stopRequested: false,
        stop: function() {
            this.isRunning = false;
            this.stopRequested = true;
            this.state = CONSTANTS.LEARNING_STATES.IDLE;
            if (API && API.abortController) {
                API.abortController.abort();
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "🛑 正在中止所有网络请求...",
                    type: "info"
                });
            }
            const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace("#", ""));
            if (toggleBtn) {
                toggleBtn.setAttribute("data-state", "stopped");
                toggleBtn.textContent = "开始学习";
            }
            EventBus.publish(CONSTANTS.EVENTS.LEARNING_STOP);
            EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, "已停止");
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "⏹️ 学习流程已停止",
                type: "warn"
            });
        },
        hasValidId: function() {
            if (CONFIG$1.IS_PORTAL || CONFIG$1.UNSUPPORTED_BRANCH) return false;
            const href = window.location.href;
            if (href.includes("pagehome/index") || document.querySelector('[module-name="nc.pagehome.index"]')) {
                return false;
            }
            const isCoursePlayerPage = window.location.href.includes("/coursePlayer");
            const isSpecialDetailPage = window.location.href.includes("/specialdetail");
            const isChannelDetailPage = window.location.href.includes("channelDetail");
            const isPudongSpecialPage = PudongHandler.identifyPage() === PudongHandler.PAGE_TYPES.COLUMN;
            const isCbeadTrainNewPage = window.location.href.includes("train-new/class-detail");
            const isCbeadPlayerPage = window.location.href.includes("study/course/detail");
            const isChannelListPage = window.location.href.includes("channelList");
            if (isChannelListPage) {
                return false;
            }
            if (isCoursePlayerPage || isSpecialDetailPage || isChannelDetailPage || isPudongSpecialPage || isCbeadTrainNewPage || isCbeadPlayerPage) {
                let id = null;
                const urlParams = new URLSearchParams(window.location.search);
                id = urlParams.get("id");
                if (!id) {
                    const hash = window.location.hash;
                    if (hash.includes("?")) {
                        const hashParams = new URLSearchParams(hash.split("?")[1]);
                        id = hashParams.get("id");
                    }
                    if (!id) {
                        const match = hash.match(/[?&]id=([^&]+)/);
                        if (match) {
                            id = match[1];
                        }
                    }
                    if (!id) {
                        const uuidMatch = hash.match(/class-detail\/([a-f0-9-]+)/);
                        if (uuidMatch) {
                            id = uuidMatch[1];
                        }
                    }
                    if (!id) {
                        const playerMatch = hash.match(/detail\/\d+&([a-f0-9-]+)&/);
                        if (playerMatch) {
                            id = playerMatch[1];
                        }
                    }
                }
                return !!id;
            }
            const urlParams = new URLSearchParams(window.location.search);
            let id = urlParams.get("id");
            if (!id) {
                const hash = window.location.hash;
                if (hash.includes("?")) {
                    const hashParams = new URLSearchParams(hash.split("?")[1]);
                    id = hashParams.get("id");
                }
                if (!id) {
                    const match = hash.match(/[?&]id=([^&]+)/);
                    if (match) {
                        id = match[1];
                    }
                }
            }
            if (!id) {
                const hasCourseElements = CONSTANTS.COURSE_SELECTORS.some(selector => document.querySelector(selector));
                if (hasCourseElements) {
                    EventBus.publish(CONSTANTS.EVENTS.LOG, {
                        message: "[校验] 虽然URL没发现ID,但页面检测到课程元素,允许启动",
                        type: "info"
                    });
                    return true;
                }
            }
            return !!id;
        },
        async startLearning() {
            try {
                const result = await FlowOrchestrator.selectAndExecute();
                if (result === CONSTANTS.FLOW_RESULTS.WAITING_FOR_USER) {
                    console.log("[Learner] 用户主动点击按钮,开始学习流程");
                    const {CbeadLearner: CbeadLearner} = await Promise.resolve().then(function() {
                        return learner;
                    });
                    await CbeadLearner.startBranchListFlow();
                    return;
                }
                if (result === false) {
                    this._resetToggleButton("页面不支持");
                    return;
                }
                if (result === true) {
                    if (!this.stopRequested) {
                        this._resetToggleButton();
                    }
                    return;
                }
            } catch (error) {
                this._handleLearningError(error);
            }
        },
        _resetToggleButton(statusText = "学习完成") {
            const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace("#", ""));
            if (toggleBtn) {
                toggleBtn.setAttribute("data-state", "stopped");
                toggleBtn.textContent = "开始学习";
            }
            EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, statusText);
        },
        _handleLearningError(error) {
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `❌ 学习流程出错: ${error.message}`,
                type: "error"
            });
            console.error("学习流程错误:", error);
            this._resetToggleButton("学习出错");
        }
    };
    var bizLearner = Object.freeze({
        __proto__: null,
        Learner: Learner,
        setAPI: setAPI
    });
    const LearningStrategies = {
        async instant_finish(context) {
            const {duration: duration} = context;
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "🚀 采用极速完成策略 - 直接冲刺",
                type: "info"
            });
            const learner = ServiceLocator.get(ServiceNames.LEARNER);
            const delay = Math.floor(Math.random() * 500 + 500);
            const steps = 5;
            const stepDelay = delay / steps;
            for (let i = 0; i < steps; i++) {
                if (learner && learner.stopRequested) {
                    EventBus.publish(CONSTANTS.EVENTS.LOG, {
                        message: "🛑 用户中断学习",
                        type: "warn"
                    });
                    return false;
                }
                await new Promise(resolve => setTimeout(resolve, stepDelay));
            }
            const finalTime = Math.max(0, duration - 30);
            if (learner && learner.stopRequested) {
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: "🛑 用户中断学习",
                    type: "warn"
                });
                return false;
            }
            const api = ServiceLocator.get(ServiceNames.API);
            return await api.reportProgressWithDelay(context.playInfo, finalTime);
        }
    };
    var bizStrategies = Object.freeze({
        __proto__: null,
        LearningStrategies: LearningStrategies
    });
    function setupDependencyInjection() {
        if (typeof window !== "undefined") {
            window.UI = UI;
            window.CbeadLearner = CbeadLearner;
        }
        ServiceLocator.register(ServiceNames.UI, UI);
        ServiceLocator.register(ServiceNames.API, API$1);
        ServiceLocator.register(ServiceNames.LEARNER, Learner);
        ServiceLocator.register(ServiceNames.CONFIG, CONFIG$1);
        setAPI(API$1);
    }
    function initScript() {
        Settings.load();
        setupDependencyInjection();
        UI.createPanel();
        detectEnvironment();
        PudongHandler.init();
        CbeadHandler.init();
        GM_registerMenuCommand("导出调试日志", UI.exportLogs, "e");
        const toggleBtn = document.getElementById(CONSTANTS.SELECTORS.TOGGLE_BTN.replace("#", ""));
        if (toggleBtn) {
            toggleBtn.addEventListener("click", () => {
                const isRunning = toggleBtn.getAttribute("data-state") === "running";
                if (isRunning) {
                    Learner.stop();
                } else {
                    Learner.stopRequested = false;
                    Learner.isRunning = true;
                    Learner.state = CONSTANTS.LEARNING_STATES.LEARNING;
                    EventBus.publish(CONSTANTS.EVENTS.LEARNING_START);
                    EventBus.publish(CONSTANTS.EVENTS.STATUS_UPDATE, "学习中...");
                    if (CONFIG$1.PUDONG_MODE) {
                        EventBus.publish("pudong:startLearning");
                        return;
                    }
                    Learner.startLearning().catch(error => {
                        EventBus.publish(CONSTANTS.EVENTS.LOG, {
                            message: `❌ 启动学习流程失败: ${error.message}`,
                            type: "error"
                        });
                        Learner.stop();
                    });
                }
            });
        }
        EventBus.publish(CONSTANTS.EVENTS.LOG, {
            message: "🚀 cela学习助手 v4.0 初始化完成",
            type: "success"
        });
        setTimeout(() => {
            checkAndStartAutoLearning();
        }, 1e3);
        setupRouteListener();
        if (CONFIG$1.CBEAD_MODE) {
            setupProgressErrorMonitor();
        }
    }
    let isAutoStarting = false;
    function checkAndStartAutoLearning() {
        if (CONFIG$1.CBEAD_MODE && window.location.href.includes("study/course/detail")) {
            console.log("[Init] 🔍 检测到企业分院播放页,准备开始学习...");
            if (isAutoStarting) {
                console.log("[Init] ⚠️ 学习任务已在进行中,跳过重复启动");
                return;
            }
            isAutoStarting = true;
            console.log("[Init] 🔒 设置防重复标志,开始学习...");
            const toggleBtn = document.getElementById("toggle-learning-btn");
            if (toggleBtn) {
                toggleBtn.setAttribute("data-state", "learning");
                toggleBtn.textContent = "停止学习";
            }
            Learner.startLearning();
            setTimeout(() => {
                isAutoStarting = false;
                console.log("[Init] 🔓 重置防重复标志(超时重置)");
            }, 6e4);
        }
    }
    function setupRouteListener() {
        if (!CONFIG$1.CBEAD_MODE) {
            console.log("[Init] 📌 非企业分院环境,不设置路由监听");
            return;
        }
        console.log("[Init] 📡 设置路由监听(企业分院 SPA 模式)");
        let lastUrl = window.location.href;
        window.addEventListener("hashchange", async () => {
            try {
                const currentUrl = window.location.href;
                console.log(`[Init] 🔄 检测到路由变化:`);
                console.log(`   - 旧 URL: ${lastUrl}`);
                console.log(`   - 新 URL: ${currentUrl}`);
                if (currentUrl.includes("study/errors/") && lastUrl.includes("study/course/detail")) {
                    console.warn("[Init] ⚠️ 检测到跳转到错误页面!");
                    console.warn("[Init] 💡 可能原因:课程不存在、无访问权限或已被删除");
                    const uuidMatch = currentUrl.match(/errors\/([a-f0-9-]+)/);
                    if (uuidMatch) {
                        const failedUuid = uuidMatch[1];
                        console.warn(`[Init] 📌 失败课程 UUID: ${failedUuid}`);
                        EventBus.publish(CONSTANTS.EVENTS.LOG, {
                            message: `❌ 课程加载失败(UUID: ${failedUuid.substring(0, 8)}...),可能无访问权限`,
                            type: "error"
                        });
                        LearningState.markFailed("课程加载失败(跳转到错误页面)");
                        setTimeout(() => {
                            if (typeof CbeadHandler !== "undefined" && CbeadHandler.returnToList) {
                                console.log("[Init] 🔄 返回列表页...");
                                const returnUrl = "#/branch-list-v";
                                CbeadHandler.returnToList(returnUrl);
                            }
                        }, 2e3);
                    }
                }
                if (currentUrl.includes("study/course/detail") && !lastUrl.includes("study/course/detail")) {
                    console.log("[Init] 🎯 导航到播放页,立即检查批量学习任务...");
                    checkAndStartAutoLearning();
                }
                if (currentUrl.includes("train-new/class-detail") || currentUrl.includes("class-detail") || currentUrl.includes("branch-list-v")) {
                    console.log("[Init] 📋 导航到列表页,触发学习流程...");
                    setTimeout(() => {
                        CbeadLearner.selectAndExecute();
                    }, 2e3);
                }
                lastUrl = currentUrl;
            } catch (error) {
                console.error("[Init] ❌ hashchange 事件处理出错:", error);
                EventBus.publish(CONSTANTS.EVENTS.LOG, {
                    message: `路由变化处理失败: ${error.message}`,
                    type: "error"
                });
            }
        });
        setInterval(() => {
            const currentUrl = window.location.href;
            if (currentUrl !== lastUrl) {
                console.log(`[Init] 🔄 检测到 URL 变化(轮询):`);
                console.log(`   - 旧 URL: ${lastUrl}`);
                console.log(`   - 新 URL: ${currentUrl}`);
                if (currentUrl.includes("study/errors/") && lastUrl.includes("study/course/detail")) {
                    console.warn("[Init] ⚠️ 检测到跳转到错误页面(轮询)!");
                    console.warn("[Init] 💡 可能原因:课程不存在、无访问权限或已被删除");
                    const uuidMatch = currentUrl.match(/errors\/([a-f0-9-]+)/);
                    if (uuidMatch) {
                        const failedUuid = uuidMatch[1];
                        console.warn(`[Init] 📌 失败课程 UUID: ${failedUuid}`);
                        EventBus.publish(CONSTANTS.EVENTS.LOG, {
                            message: `❌ 课程加载失败(UUID: ${failedUuid.substring(0, 8)}...),可能无访问权限`,
                            type: "error"
                        });
                        LearningState.markFailed("课程加载失败(跳转到错误页面)");
                        setTimeout(() => {
                            if (typeof CbeadHandler !== "undefined" && CbeadHandler.returnToList) {
                                console.log("[Init] 🔄 返回列表页...");
                                const returnUrl = "#/branch-list-v";
                                CbeadHandler.returnToList(returnUrl);
                            }
                        }, 2e3);
                    }
                }
                if (currentUrl.includes("study/course/detail") && !lastUrl.includes("study/course/detail")) {
                    console.log("[Init] 🎯 导航到播放页,立即检查批量学习任务...");
                    checkAndStartAutoLearning();
                }
                if (currentUrl.includes("train-new/class-detail") || currentUrl.includes("class-detail") || currentUrl.includes("branch-list-v")) {
                    console.log("[Init] 📋 导航到列表页,触发学习流程(轮询)...");
                    setTimeout(() => {
                        CbeadLearner.selectAndExecute();
                    }, 2e3);
                }
                lastUrl = currentUrl;
            }
        }, 1e3);
    }
    function setupProgressErrorMonitor() {
        console.log("[Init] 📡 设置进度错误监听器(企业分院专用)...");
        LearningState.reset();
        const skipCurrentCourse = reason => {
            if (LearningState.isFailed()) {
                console.log("[ProgressError] ⚠️ 跳过已触发,忽略重复调用");
                return;
            }
            LearningState.markFailed(reason);
            console.warn(`[ProgressError] 🚨 检测到进度上报失败,标记当前课程为失败: ${reason}`);
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: `❌ 进度上报失败(${reason}),标记当前课程为失败`,
                type: "error"
            });
            EventBus.publish(CONSTANTS.EVENTS.LOG, {
                message: "💡 服务器无法记录学习进度,继续播放无意义",
                type: "info"
            });
            EventBus.publish("course:failed", {
                reason: `进度上报失败: ${reason}`
            });
            setTimeout(() => {
                if (typeof Learner !== "undefined" && Learner.state === CONSTANTS.LEARNING_STATES.LEARNING) {
                    console.log("[ProgressError] 🛑 停止当前学习流程...");
                    Learner.stop();
                }
                console.log("[ProgressError] ✅ 已标记当前课程为失败");
            }, 500);
        };
        window.celaAutoResetCourseFail = () => {
            LearningState.reset();
        };
        const originalXHROpen = XMLHttpRequest.prototype.open;
        const originalXHRSend = XMLHttpRequest.prototype.send;
        XMLHttpRequest.prototype.open = function(method, url) {
            this._method = method;
            this._url = url;
            return originalXHROpen.apply(this, arguments);
        };
        XMLHttpRequest.prototype.send = function() {
            if (this._url && this._url.includes("update-progress")) {
                this.addEventListener("load", function() {
                    if (this.status === 422) {
                        console.error("[ProgressError] POST 422:", this._url);
                        skipCurrentCourse("422 Unprocessable Content");
                    }
                });
            }
            return originalXHRSend.apply(this, arguments);
        };
        const originalFetch = window.fetch;
        window.fetch = function(url, options) {
            return originalFetch.apply(this, arguments).then(response => {
                if (typeof url === "string" && url.includes("update-progress") && response.status === 422) {
                    console.error("[ProgressError] POST 422:", url);
                    skipCurrentCourse("422 Unprocessable Content");
                }
                return response;
            });
        };
        console.log("[Init] ✅ 进度错误监听器已启动(422 错误将自动跳过课程)");
    }
    function immediateMuteAllVideos() {
        console.log("[Init] 🔇 立即静音模式启动...");
        const videos = document.querySelectorAll("video");
        console.log(`[Init] 📹 找到 ${videos.length} 个现有的 video 元素`);
        videos.forEach((video, index) => {
            video.muted = true;
            video.volume = 0;
            if (window.player && typeof window.player.muted === "function") {
                try {
                    window.player.muted(true);
                } catch (e) {}
            }
            console.log(`[Init] 🔇 video #${index + 1} 已静音`);
        });
        const audios = document.querySelectorAll("audio");
        console.log(`[Init] 🎵 找到 ${audios.length} 个现有的 audio 元素`);
        audios.forEach((audio, index) => {
            audio.muted = true;
            audio.volume = 0;
            audio.pause();
            console.log(`[Init] 🔇 audio #${index + 1} 已静音并暂停`);
        });
        let pollCount = 0;
        const maxPolls = 100;
        const pollInterval = setInterval(() => {
            pollCount++;
            const allVideos = document.querySelectorAll("video");
            let hasUnmuted = false;
            allVideos.forEach(video => {
                if (!video.muted || video.volume !== 0) {
                    video.muted = true;
                    video.volume = 0;
                    if (!hasUnmuted) {
                        console.log(`[Init] 🔇 轮询发现未静音的 video,立即静音 (第${pollCount}次)`);
                        hasUnmuted = true;
                    }
                }
                if (window.player && typeof window.player.muted === "function") {
                    try {
                        if (!window.player.muted()) {
                            window.player.muted(true);
                        }
                    } catch (e) {}
                }
            });
            const allAudios = document.querySelectorAll("audio");
            allAudios.forEach(audio => {
                if (!audio.muted || audio.volume !== 0) {
                    audio.muted = true;
                    audio.volume = 0;
                    audio.pause();
                    console.log(`[Init] 🔇 轮询发现未静音的 audio,立即静音 (第${pollCount}次)`);
                }
            });
            if (pollCount >= maxPolls) {
                clearInterval(pollInterval);
                console.log("[Init] ✅ 高频轮询完成");
            }
        }, 100);
        console.log("[Init] 📡 高频轮询已启动(每100ms,持续10秒)");
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeName === "VIDEO") {
                        console.log("[Init] 🔇 MutationObserver: 检测到新创建的 video 元素,立即静音并暂停");
                        node.muted = true;
                        node.volume = 0;
                        node.autoplay = false;
                        node.pause();
                    }
                    if (node.nodeName === "AUDIO") {
                        console.log("[Init] 🔇 MutationObserver: 检测到新创建的 audio 元素,立即静音并暂停");
                        node.muted = true;
                        node.volume = 0;
                        node.autoplay = false;
                        node.pause();
                    }
                    if (node.querySelectorAll) {
                        const newVideos = node.querySelectorAll("video");
                        newVideos.forEach(video => {
                            console.log("[Init] 🔇 MutationObserver: 检测到新创建的子 video 元素,立即静音并暂停");
                            video.muted = true;
                            video.volume = 0;
                            video.autoplay = false;
                            video.pause();
                        });
                        const newAudios = node.querySelectorAll("audio");
                        newAudios.forEach(audio => {
                            console.log("[Init] 🔇 MutationObserver: 检测到新创建的子 audio 元素,立即静音并暂停");
                            audio.muted = true;
                            audio.volume = 0;
                            audio.autoplay = false;
                            audio.pause();
                        });
                    }
                });
            });
        });
        observer.observe(document.documentElement, {
            childList: true,
            subtree: true
        });
        console.log("[Init] ✅ MutationObserver 已启动,将持续监听并静音新媒体元素");
        console.log("[Init] 🛡️ 检查遮罩并点击按钮...");
        const maskCheck = detectMask();
        if (maskCheck.exists) {
            console.log(`[Init] 🛡️ 检测到 ${maskCheck.masks.length} 个遮罩,尝试点击按钮...`);
            const clickResult = clickMaskButton();
            console.log(`[Init] 🖱️ 已点击 ${clickResult.clicked} 个遮罩按钮`);
        }
        startMaskObserver();
    }
    const hasVideoElement = document.querySelector("video") !== null;
    if (window.location.href.includes("study/course/detail") && hasVideoElement) {
        immediateMuteAllVideos();
    }
    setTimeout(initScript, 1e3);
    exports.API = API$1;
    exports.ApiFactory = ApiFactory;
    exports.CONFIG = CONFIG$1;
    exports.CONSTANTS = CONSTANTS;
    exports.CbeadHandler = CbeadHandler;
    exports.CbeadLearner = CbeadLearner;
    exports.CourseAdapter = CourseAdapter;
    exports.EventBus = EventBus;
    exports.Learner = Learner;
    exports.LearningStrategies = LearningStrategies;
    exports.PudongHandler = PudongHandler;
    exports.RequestQueue = RequestQueue;
    exports.Settings = Settings;
    exports.UI = UI;
    exports.Utils = Utils;
    exports.detectEnvironment = detectEnvironment;
    return exports;
})({});