
let assetList_file=[
'js/kw/kw3.2.js', 
"js/codemirror/5.65.17/codemirror.min.css",
"js/codemirror/5.65.17/show-hint.min.css",
"js/codemirror/5.65.17/matchesonscrollbar.css",
"js/codemirror/5.65.17/codemirror.min.js",
"js/codemirror/5.65.17/javascript.min.js",
"js/codemirror/5.65.17/matchbrackets.min.js",
"js/codemirror/5.65.17/searchcursor.js",
"js/codemirror/5.65.17/match-highlighter.min.js",
"js/codemirror/5.65.17/comment.min.js",
"js/codemirror/5.65.17/continuecomment.min.js",
"js/codemirror/5.65.17/show-hint.min.js",
"js/codemirror/5.65.17/javascript-hint.min.js",
"js/codemirror/5.65.17/ggbppt3.3.css",
] 


let assetList=[];
(function() {
function detectBasePath(offline, online, callback) {
    const testScript = document.createElement("script");
    testScript.src = './js/v.js';
    testScript.onload = () => callback(offline);
    testScript.onerror = () => callback(online);
    document.head.appendChild(testScript);
}
detectBasePath(offline_libpath, online_libpath, base_path => {
    assetList = assetList_file.map(name => base_path + name);
    console.log("最终资源列表已初始化:", assetList);
});

})();




let isAssetsLoaded = false;
let editor = null; 
let initialCode = ``;

function showggb_code(){
if (!isAssetsLoaded) {
    // this.innerText = "正在加载资源...";
    showSuccess('正在加载资源...');
    this.disabled = true;

    loadAssetsSequentially(assetList, () => {
        isAssetsLoaded = true;
        this.disabled = false;
        initCodeMirror();
        initCodeMirror2();
        showPopup();
    });

} else {
    showSuccess('成功显示...');
    showPopup();
}
}



// --- 3. 资源加载器 (核心逻辑) ---
function loadAssetsSequentially(list, callback) {
let index = 0;
function loadNext() {
    if (index >= list.length) {
        callback();
        return;
    }
    const url = list[index];
    index++;
    loadSingleAsset(url, loadNext);
}
loadNext();
}

function loadSingleAsset(url, onLoad) {
    let element;
    if (url.endsWith(".css")) {
        element = document.createElement("link");
        element.rel = "stylesheet";
        element.href = url;
    } else {
        element = document.createElement("script");
        element.src = url;
    }
    element.onload = onLoad;
    element.onerror = () => {
        console.error("加载失败:", url);
        onLoad(); // 即使失败也继续，防止卡死
    };
    document.head.appendChild(element);
}

// --- 4. 初始化功能 (资源加载后执行) ---
function initCodeMirror() {
    (function() {
"use strict";
// const openBrackets = "([{";
// const closeBrackets = ")]}";
// const bracketPairs = { '(': ')', '[': ']', '{': '}' };
// const colorCount = 8;

CodeMirror.defineMode("javascript-rb", function(config) {
    const jsMode = CodeMirror.getMode(config, "javascript");
    const mode = {
        startState: function() {
            return { bracketStack: [], jsState: CodeMirror.startState(jsMode) };
        },
        copyState: function(state) {
            return { bracketStack: state.bracketStack.slice(), jsState: CodeMirror.copyState(jsMode, state.jsState) };
        },
        token: function(stream, state) {
            const char = stream.peek();
            if ("([{".indexOf(char) > -1) {
                const level = state.bracketStack.length;
                state.bracketStack.push(char);
                stream.next();
                return `bracket-level-${level % 8}`;
            }
            if (")]}".indexOf(char) > -1) {
                stream.next();
                const stackSize = state.bracketStack.length;
                if (stackSize > 0 && { '(':')', '[':']', '{':'}' }[state.bracketStack[stackSize - 1]] === char) {
                    state.bracketStack.pop();
                    const level = state.bracketStack.length;
                    return `bracket-level-${level % 8}`;
                } else {
                    return "mismatch-bracket";
                }
            }
            return jsMode.token(stream, state.jsState);
        }
    };
if (jsMode.lineComment) mode.lineComment = jsMode.lineComment;
if (jsMode.blockCommentStart) mode.blockCommentStart = jsMode.blockCommentStart;
if (jsMode.blockCommentEnd) mode.blockCommentEnd = jsMode.blockCommentEnd;
return mode;
});


editor = CodeMirror.fromTextArea(document.getElementById("ggbInput"), {
mode: "javascript-rb",
lineNumbers: true,
matchBrackets: true,
indentUnit: 2,
tabSize: 2,
extraKeys: {
"Ctrl-Q": "toggleComment",        // 行注释
"Cmd-Q": "toggleComment",         // Mac
    "Ctrl-D": function(cm) {
        const selections = cm.listSelections();
        cm.operation(function() {
            selections.forEach(sel => {
                let from = sel.from(), to = sel.to();
                if (from.line === to.line && from.ch === to.ch) {
                    const lineText = cm.getLine(from.line);
                    cm.replaceRange(lineText + "\n", { line: from.line + 1, ch: 0 });
                } else {
                    const text = cm.getRange(from, to);
                    cm.replaceRange(text, { line: to.line + 1, ch: 0 });
                }
            });
        });
    }
},
highlightSelectionMatches: {
    showToken: /\w/,
    annotateScrollbar: true
},
hintOptions: { completeSingle: false }, // 不自动选中第一个
});

setTimeout(() => { editor.refresh(); }, 100);

const fontslider1 = document.getElementById("fontslider1");
editor.getWrapperElement().style.fontSize = fontslider1.value + "px";
editor.refresh(); 
let ffsize = fontslider1.value;
fontslider1.addEventListener("input", function() {
editor.getWrapperElement().style.fontSize = this.value + "px";
editor.refresh();
ffsize = fontslider1.value;
});

// editor.setValue(initialCode);

////////////////////////////////////
function customHint(cm) {
const cur = cm.getCursor();
const token = cm.getTokenAt(cur);
let word = token.string;
let from = CodeMirror.Pos(cur.line, token.start);
let to = CodeMirror.Pos(cur.line, token.end);

if (token.type === "comment") {
    const match = word.match(/([\w\u4e00-\u9fa5]+)$/);
    if (match) {
    word = match[1];
    const startOffset = token.end - match[1].length;
    from = CodeMirror.Pos(cur.line, startOffset);
    } else {
    word = "";
    }
} else {
    word = word.replace(/[^\w\u4e00-\u9fa5]/g, "");
}
word = word.toLowerCase();

const matches = functionList.filter(f => f.kw.toLowerCase().includes(word));
const listSource = matches.length ? matches : functionList;

const list = listSource.map(f => ({
    text: f.text,
    displayText: f.kw,
    render: function (el) {
    el.style.display = "flex";
    el.style.justifyContent = "space-between";
    el.style.alignItems = "flex-start";
    el.style.fontSize = `${ffsize}px`;
    el.style.maxWidth = "900px";
    el.style.whiteSpace = "normal";
    el.style.wordBreak = "break-word";
    el.style.padding = "3px 6px";
    
    const left = document.createElement("div");
    left.textContent = f.kw;
    left.style.flex = "1";
    left.style.fontWeight = "bold";
    left.style.marginRight = "12px";
    left.style.color = "#222";
    left.style.minWidth = "280px";
    left.style.maxWidth = "400px";
    
    const right = document.createElement("div");
    let htmlDisplay = f.displayText?.split("<br>")[0]?.split("||||")[0] || "";
    right.innerHTML = htmlDisplay;
    right.style.flex = "1";
    right.style.color = "#333";
    right.style.textAlign = "left";
    right.style.overflow = "hidden";
    right.style.textOverflow = "ellipsis";
    right.style.maxHeight = "80px";
    right.style.opacity = 0.9;
    

    left.style.zIndex = 99999;
    right.style.zIndex = 99999;
    el.style.zIndex = 99999;
    el.appendChild(left);
    el.appendChild(right);
    }
}));

return { list, from, to };
}



editor.on("inputRead", function (cm, change) {
if (change.text[0].match(/[\w\u4e00-\u9fa5]/)) {
    cm.showHint({ hint: customHint, completeSingle: false });
}
initialCode = editor.getValue();
});
initialCode = editor.getValue();

let tooltipEl = null;
let hideTimer = null;

function showTooltipAt(tokenPos, htmlContent) {
removeTooltip(); 
const coords = editor.charCoords(tokenPos, "page");

tooltipEl = document.createElement("div");
tooltipEl.style.position = "absolute";
tooltipEl.style.left = coords.left + "px";
tooltipEl.style.background = "rgba(240,240,240,1)";
tooltipEl.style.color = "rgba(0,0,0,1)";
tooltipEl.style.padding = "8px";
tooltipEl.style.borderRadius = "6px";
tooltipEl.style.fontSize = `${ffsize}px`;
tooltipEl.style.maxWidth = "800px";
tooltipEl.style.maxHeight = "600px";
tooltipEl.style.zIndex = 9999;
tooltipEl.style.whiteSpace = "normal";
tooltipEl.style.overflowY = "auto";
tooltipEl.style.overflowX = "hidden";
tooltipEl.style.boxShadow = "0 2px 8px rgba(0,0,0,0.5)";
tooltipEl.style.userSelect = "text";
tooltipEl.innerHTML = htmlContent;

tooltipEl.addEventListener("mouseenter", () => {
    if (hideTimer) {
        clearTimeout(hideTimer);
        hideTimer = null;
    }
});
tooltipEl.addEventListener("mouseleave", () => {
    hideTimer = setTimeout(removeTooltip, 100);
});

document.body.appendChild(tooltipEl);

// 🔽 关键：在插入 DOM 后重新计算 top，让 tooltip 出现在上方
const tooltipHeight = tooltipEl.offsetHeight;
tooltipEl.style.top = (coords.top - tooltipHeight) + "px";  // 8px 为间距
}


// 移除 tooltip
function removeTooltip() {
if (tooltipEl) {
    tooltipEl.remove();
    tooltipEl = null;
}
if (hideTimer) {
    clearTimeout(hideTimer);
    hideTimer = null;
}
}

// 在 editor 中鼠标悬浮事件
editor.getWrapperElement().addEventListener("mouseover", (e) => {
const pos = editor.coordsChar({ left: e.clientX, top: e.clientY });
const token = editor.getTokenAt(pos);

if (!token.string) return;

const match = functionList.find(f => f.text === token.string);
if (match) {
    const htmlContent = `
    <div style="margin-top:4px;">
    ${match.kw}  &nbsp;&nbsp;
            <a href="https://translate.yandex.com/translate?lang=en-zh&url=https://geogebra.github.io/docs/manual/en/commands/${match.text}" target="_blank">中文翻译文档</a> &nbsp;&nbsp;
            <a href="https://geogebra.github.io/docs/manual/en/commands/${match.text}" target="_blank">英文官方文档</a>  
        </div>
        <div>${match.displayText}</div>
    `;
    showTooltipAt({ line: pos.line, ch: token.start }, htmlContent);
}
});

// 鼠标离开 editor token
editor.getWrapperElement().addEventListener("mouseout", (e) => {
if (!tooltipEl) return;

// 延迟隐藏 tooltip
hideTimer = setTimeout(removeTooltip, 100);
});

})();

    }

function showPopup() {
    const popup = document.getElementById('customPopup');
    popup.style.display = 'flex';
    // 每次显示时刷新一下编辑器，防止样式错乱
    if (editor) editor.refresh(); 
}

// // --- 5. 页面交互函数 (补充缺失的函数) ---
function runGgbCode() {
    if (!editor) return;
    const code = editor.getValue();
    let cleaned = code.replace(/\s*\/\/.*$/gm, '').replace(/,+\s*$/gm, '').replace(/;+\s*$/gm, '').replace(/^\s*[\r\n]/gm, '').trim();
    let lines = cleaned.split("\n");
    lines.forEach(line => {
    ggbApplet.evalCommand(line);
    });
}

function changeFontSize(size) {
    const cmDiv = document.querySelector('.CodeMirror');
    if(cmDiv) cmDiv.style.fontSize = size + 'px';
}

// --- 6. 弹窗拖拽逻辑 (不需要 Lazy Load，因为代码量很小) ---
// 放在这里比注入到 <script> 标签里更稳定、好调试
(function initDrag() {
    const popup = document.getElementById('customPopup');
    const header = document.getElementById('popupHeader');
    const closeBtn = document.getElementById('closePopupBtn');
    
    let isDragging = false;
    let startX, startY;
    let rafId = null;

    closeBtn.onclick = () => popup.style.display = 'none';

    header.addEventListener('mousedown', (e) => {
        isDragging = true;
        const rect = popup.getBoundingClientRect();
        
        // 将 transform 居中转为绝对定位，防止跳动
        if (window.getComputedStyle(popup).transform !== 'none') {
            popup.style.left = rect.left + 'px';
            popup.style.top = rect.top + 'px';
            popup.style.transform = 'none';
            popup.style.margin = '0';
        }

        startX = e.clientX - rect.left;
        startY = e.clientY - rect.top;
        e.preventDefault(); // 防止选中文本
    });

    document.addEventListener('mousemove', (e) => {
        if (!isDragging) return;
        if (rafId) return;

        rafId = requestAnimationFrame(() => {
            popup.style.left = (e.clientX - startX) + 'px';
            popup.style.top = (e.clientY - startY) + 'px';
            rafId = null;
        });
    });

    document.addEventListener('mouseup', () => {
        isDragging = false;
        if (rafId) {
            cancelAnimationFrame(rafId);
            rafId = null;
        }
    });
})();




////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////

let editor2 = null; 
let initialCode2 = ``;

function showjs_code(){
if (!isAssetsLoaded) {
        // this.innerText = "正在加载资源...";
        showSuccess('正在加载资源...');
        this.disabled = true;

        loadAssetsSequentially(assetList, () => {
            isAssetsLoaded = true;
            this.disabled = false;
            initCodeMirror2();
            initCodeMirror();
            showPopup2();
        });

    } else {
        showSuccess('成功显示...');
        showPopup2();
    }
}


function initCodeMirror2() {
"use strict";
const editor2 = CodeMirror.fromTextArea(document.getElementById("jsInput"), {
mode: "javascript",
lineNumbers: true,
matchBrackets: true,
indentUnit: 2,
tabSize: 2,
extraKeys: {
"Ctrl-Q": "toggleComment",        // 行注释
"Cmd-Q": "toggleComment",         // Mac
    "Ctrl-D": function(cm) {
        const selections = cm.listSelections();
        cm.operation(function() {
            selections.forEach(sel => {
                let from = sel.from(), to = sel.to();
                if (from.line === to.line && from.ch === to.ch) {
                    const lineText = cm.getLine(from.line);
                    cm.replaceRange(lineText + "\n", { line: from.line + 1, ch: 0 });
                } else {
                    const text = cm.getRange(from, to);
                    cm.replaceRange(text, { line: to.line + 1, ch: 0 });
                }
            });
        });
    }
},
highlightSelectionMatches: {
    showToken: /\w/,
    annotateScrollbar: true
},
hintOptions: { completeSingle: false }, // 不自动选中第一个
});

setTimeout(() => { editor2.refresh(); }, 100);

const fontslider2 = document.getElementById("fontslider2");
editor2.getWrapperElement().style.fontSize = fontslider2.value + "px";
editor2.refresh(); 
fontslider2.addEventListener("input", function() {
editor2.getWrapperElement().style.fontSize = this.value + "px";
editor2.refresh();
setTimeout(() => { editor2.refresh(); }, 100);
});

// editor.setValue(initialCode);
// initialCode2 = editor2.getValue();
editor2.on("inputRead", function() {
initialCode2 = editor2.getValue();
});
initialCode2 = editor2.getValue();

}

function showPopup2() {
const popup2 = document.getElementById('customPopup2');
popup2.style.display = 'flex';
// 每次显示时刷新一下编辑器，防止样式错乱
if (editor2) editor2.refresh(); 
}




(function initDrag2() {
const popup2 = document.getElementById('customPopup2');
const header2 = document.getElementById('popupHeader2');
const closeBtn2 = document.getElementById('closePopupBtn2');

let isDragging = false;
let startX, startY;
let rafId = null;

closeBtn2.onclick = () => popup2.style.display = 'none';

header2.addEventListener('mousedown', (e) => {
    isDragging = true;
    const rect = popup2.getBoundingClientRect();
    
    // 将 transform 居中转为绝对定位，防止跳动
    if (window.getComputedStyle(popup2).transform !== 'none') {
        popup2.style.left = rect.left + 'px';
        popup2.style.top = rect.top + 'px';
        popup2.style.transform = 'none';
        popup2.style.margin = '0';
    }

    startX = e.clientX - rect.left;
    startY = e.clientY - rect.top;
    e.preventDefault(); // 防止选中文本
});

document.addEventListener('mousemove', (e) => {
    if (!isDragging) return;
    if (rafId) return;

    rafId = requestAnimationFrame(() => {
        popup2.style.left = (e.clientX - startX) + 'px';
        popup2.style.top = (e.clientY - startY) + 'px';
        rafId = null;
    });
});

document.addEventListener('mouseup', () => {
    isDragging = false;
    if (rafId) {
        cancelAnimationFrame(rafId);
        rafId = null;
    }
});
})();



function runJsCode(){
if(!ggbApplet) return;
try {
    const fn = new Function("api", initialCode2);
    fn(ggbApplet);
} catch(err){
    alert("执行 JS 代码出错: " + err.message);
}
}

