半自动填写插件

半自动填写插件,匹配并尝试填写网页,填写失败则写入剪贴板。可用于在线网页填写简历等等

// ==UserScript==
// @name         半自动填写插件
// @namespace    https://github.com/balala8/semi_autofill_plugin/
// @version      0.2
// @description  半自动填写插件,匹配并尝试填写网页,填写失败则写入剪贴板。可用于在线网页填写简历等等
// @author       obalala
// @match        *://*/*
// @icon         https://raw.githubusercontent.com/balala8/semi_autofill_plugin/main/logo-256.ico
// @run-at       document-end
// @license MIT
// ==/UserScript==


let information = {
    "姓名": ["张三"],
    "性别": ["男"],
    "身高": ["180"],
    "体重": ["70"],
    "籍贯": ["北京"],
    "民族": ["汉族"],
    "婚姻状况": ["未婚"],
    "职业": ["学生"],
    "微信号": ["test"],
    "邮箱": ["[email protected]"],
    "手机电话号码": ["12345678901"],
    "证件类型": ["身份证"],
    "证件号码": ["123456789012345678"],
    "当前地址": ["北京市海淀区"],
    "户口所在地": ["北京市海淀区"],
    "外语分数": ["750"],
    "紧急联系人姓名": ["李四"],
    "紧急联系人电话": ["12345678901"],
    "学历": ["本科", "硕士"],
    "毕业学校": ["北京大学", "清华大学"],
    "院系": ["计算机系", "软件工程系"],
    "专业": ["计算机", "软件工程"],
    "导师": ["张三", "李四"],
    "实验室": ["张三实验室", "李四实验室"],
    "研究方向": ["计算机视觉", "机器学习"],
    "论文": ["论文1", "论文2"],
    "获奖": ["奖项1", "奖项2"],
    "竞赛": ["竞赛1", "竞赛2"],
    "任职公司": ["公司1", "公司2"],
    "职位": ["职位1", "职位2"],
    "任职描述": ["描述1", "描述2"],
    "项目名称": ["项目1", "项目2"],
    "项目描述": ["描述1", "描述2"],
    "项目职责": ["职责1", "职责2"],
    "项目角色": ["技术1", "技术2"],
}

const popupWindow = document.createElement('div');
popupWindow.className = 'popup-window';
popupWindow.style.display = 'none';
popupWindow.style.position = 'fixed';
popupWindow.style.backgroundColor = 'rgb(46, 196, 182)';
popupWindow.style.zIndex = '9999';
document.body.appendChild(popupWindow);

var alertBox = document.createElement("div");
alertBox.textContent = "这是一个提示框";
alertBox.style.position = "fixed";
alertBox.style.top = "50%";
alertBox.style.left = "50%";
alertBox.style.transform = "translate(-50%, -50%)";
alertBox.style.backgroundColor = 'rgb(46, 196, 182)';
alertBox.style.padding = "10px";
alertBox.style.border = "1px solid rgb(46, 196, 182)";
alertBox.style.borderRadius = "5px";
alertBox.style.transition = "opacity 0.5s";
alertBox.style.display = "none";
alertBox.style.zIndex = '9999';
document.body.appendChild(alertBox);

let uponElement = null;

// 在页面加载后3秒执行
setTimeout(function() {
    // const inputElements = document.querySelectorAll('input[type="text"], textarea');
    // inputElements.forEach(input => {
    //     input.addEventListener('click', popupWindowClick);
    // });

    document.addEventListener('click', function(event) {
        if (event.target.tagName === 'INPUT' && event.target.type==='text' || event.target.tagName === 'TEXTAREA') {
            popupWindowClick(event);
        }
    });
    

    // 监听页面滚动事件
    window.addEventListener('scroll', function() {
        popupWindow.style.display = 'none';
    });
}, 3000);


// 悬浮窗口添加点击事件监听器
function popupWindowClick(event) {
    const clicked = event.target;
    uponElement = clicked;
    const inputRect = clicked.getBoundingClientRect();
    let contents = "-1"
    // 尝试从placehollder中获取关键字
    if (uponElement.placeholder !== '') {
        let keyword = uponElement.placeholder;
        contents = keyword2index(keyword);
    }
    // 从placeholder中获取失败,从dom树中获取关系上最近且排版上最近的元素
    if (contents == '-1') {
        contents = getNearLabelElement(uponElement)
    }
    if (contents == '-1') {
        showText("未找到匹配的信息");
        return;
    }

    while (popupWindow.firstChild){
        popupWindow.removeChild(popupWindow.firstChild);
    }
    for (const line of contents) {
        const div = document.createElement('div');
        div.style.backgroundColor = 'white';
        div.style.padding = '10px';
        div.style.margin = '5px';
        div.style.border = '1px solid white';
        div.style.cursor = 'pointer';
        div.style.color = 'rgb(46, 196, 182)';
        div.textContent = line;
        div.addEventListener('click', answerClick);
        popupWindow.appendChild(div);
    }
    popupWindow.style.position = 'absolute';
    popupWindow.style.top = (event.clientY + window.scrollY) + 'px';
    popupWindow.style.left = (event.clientX + window.scrollX) + 'px';
    popupWindow.style.display = 'flex'
}

// 答案item被点击事件监听器,点击选择答案并隐藏popupWindow
function answerClick(event) {
    const clickedDiv = event.target;
    const text = clickedDiv.textContent;
    console.log(`Clicked: ${text}`);
    if (text !== "") {
        uponElement.value = text;
    }
    uponElement.focus();
    popupWindow.style.display = 'none';

    var clipboardItem = new ClipboardItem({ "text/plain": new Blob([text], { type: "text/plain" }) });
    navigator.clipboard.write([clipboardItem]).then(function () {
        showText("已成功复制到剪贴板");
    }).catch(function (err) {
        console.error("复制到剪贴板失败: ", err);
        showText("复制到剪贴板失败");
    });

}

// 根据关键字获取自身信息
function keyword2index(keyword) {
    keyword = trimKeyword(keyword);
    if (keyword === false) {
        return "-1";
    }
    let maxSimilarity = 0;
    let mostSimilarKey = null;
    for (const key in information) {
        const similarity = calculateJaccardSimilarity(keyword, key);
        if (similarity > maxSimilarity) {
            maxSimilarity = similarity;
            mostSimilarKey = key;
        }
    }
    if (maxSimilarity == 0) {
        return "-1";
    }
    console.log("最匹配的 key 是:", mostSimilarKey, "相似度:", maxSimilarity, "值:", information[mostSimilarKey]);
    return information[mostSimilarKey];
}

function getNearLabelElement(element) {
    const siblings = Array.from(element.parentNode.children);
    siblings.sort((a, b) => {
        const distanceToA = Math.abs(a.getBoundingClientRect().top - element.getBoundingClientRect().top);
        const distanceToB = Math.abs(b.getBoundingClientRect().top - element.getBoundingClientRect().top);
        return distanceToA - distanceToB;
    });

    for (let i = 0; i < siblings.length; i++) {
        let html = siblings[i].innerHTML;
        let htmlText = html.replace(/<[^>]+>/g, '');
        if (htmlText == null || htmlText == undefined || htmlText == "") {
            continue;
        }
        console.log("htmlText:", htmlText);
        let res = keyword2index(htmlText);
        console.log("res:", res);
        if (res != "-1") {
            return res;
        }
    }
    return getNearLabelElement(element.parentNode);
}

function trimKeyword(keyword) {
    keyword = keyword.replace(/请|输入|\s/g, '');
    if (keyword === null || keyword === undefined || keyword === '') {
        return false;
    }
    return keyword;
}

// 计算 Jaccard 相似度
function calculateJaccardSimilarity(str1, str2) {
    const set1 = new Set(str1.split(''));
    const set2 = new Set(str2.split(''));
    const intersection = new Set([...set1].filter(x => set2.has(x)));
    const union = new Set([...set1, ...set2]);
    return intersection.size / union.size;
}

function showText(text) {
    alertBox.textContent = text;
    alertBox.style.display = "flex";
    alertBox.style.opacity = "1";
    setTimeout(function () {
        alertBox.style.opacity = "0";
        setTimeout(function () {
            alertBox.style.display = "none";
        }, 500);
    }, 500);
}