ViSearch/Google of Google

Beta version.ViSearch/Google of Google...Beyond the AI with eyes

当前为 2023-02-09 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         ViSearch/Google of Google
// @namespace    https://github.com/new4u
// @version      draft0.15.3
// @description  Beta version.ViSearch/Google of Google...Beyond the AI with eyes
// @author       new4u本爷有空
// @connect    google.com
// @connect    google.com.hk
// @connect    google.com.jp
// @include    *://encrypted.google.*/search*
// @include    *://*.google*/search*
// @include    *://*.google*/webhp*
// @match        *www.google.com*
// @icon        https://upload.wikimedia.org/wikipedia/commons/thumb/3/36/WikiProject_Sociology_Babel_%28Deus_WikiProjects%29.png/240px-WikiProject_Sociology_Babel_%28Deus_WikiProjects%29.png
// @require     https://unpkg.com/[email protected]/build/d3.min.js
// @require     https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.9.1/d3-tip.min.js
// @require     http://cdn.bootcss.com/jquery/2.1.4/jquery.min.js
// @require     http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js
// @resource    http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css
// @grant        none
// @copyright    2015-2023, new4u
// @license      GPL-3.0-only
// ==/UserScript==
let styleSheet = `
body {
    // background-color: #272b30;
    padding: 30px 40px;
    // text-align: center;
    font-family: OpenSans-Light, PingFang SC, Hiragino Sans GB, Microsoft Yahei, Microsoft Jhenghei, sans-serif;
}

.links line {
    stroke: rgb(255, 255, 255);
    stroke-opacity: 0.5;
}

.links line.inactive {
    stroke-opacity: 0;
}

.nodes circle {
    stroke: #fff;
    stroke-width: 1.5px;
}

.nodes circle:hover {
    cursor: pointer;
}

.nodes circle.inactive {
    display: none !important;
}


@media screen and (max-width: 600px) {
    .text {
      font-size: 8px; /* 当屏幕宽度小于600px时 最小字体为8px */
    }
  }

  .texts text {
    font-size: 12px; /* 最小字体为12px */
    min-font-size: 8px; /* 最小字体不能小于8px */
    max-font-size: 36px;
    font-weight:bold;
    font-family:"Microsoft YaHei";
    text-shadow: 0 0 3px #fff, 0 0 10px #fff;
  }



.texts text:hover {
    cursor: pointer;
}

.texts text.inactive {
    display: none !important;
}

#indicator {
    position: absolute;
    left: 60px;
    bottom: 120px;
}

#indicator {
    text-align: left;
    color: #f2f2f2;
    font-size: 12px;

}

#indicator > div {
    margin-bottom: 4px;
}

#indicator span {
    display: inline-block;
    width: 30px;
    height: 14px;
    position: relative;
    top: 2px;
    margin-right: 8px;
}

#mode {
    position: absolute;
    top: 160px;
    left: 60px;
}

#mode span {
    display: inline-block;
    border: 1px solid #fff;
    color: #fff;
    padding: 6px 10px;
    border-radius: 4px;
    font-size: 14px;
    transition: color, background-color .3s;
    -o-transition: color, background-color .3s;
    -ms-transition: color, background-color .3s;
    -moz-transition: color, background-color .3s;
    -webkit-transition: color, background-color .3s;
}

#mode span.active,
#mode span:hover {
    background-color: #fff;
    color: #333;
    cursor: pointer;
}

#search1 input {
    position: absolute;
    top: 220px;
    left: 60px;
    color: #fff;
    border: none;
    outline: none;
    box-shadow: none;
    width: 200px;
    background-color: #666;
}

#info {
    position: absolute;
    bottom: 40px;
    right: 30px;
    text-align: right;
    width: 270px;
}

#info h4 {
    color: #fff;
}

#info p {
    color: #fff;
    font-size: 12px;
    margin-bottom: 5px;
}

#info p span {
    color: #888;
    margin-right: 10px;
}

#svg g.row:hover {
    stroke-width: 1px;
    stroke: #fff;
}
`;
const xmlns = "http://www.w3.org/2000/svg";
const width = 800;
const height = 560;
const colors = ['#6ca46c', '#4e88af', '#c72eca', '#d2907c'];
//临时半径R大小控制,之后改成连接数量影响大小
const sizes = [30, 20, 20, 0.5];
const forceRate = 1000;

const searchtext = document.querySelectorAll("input" && ".gLFyf")[1].value;
let s = document.createElement('style');
s.type = "text/css";
s.innerHTML = styleSheet;
(document.head || document.documentElement).appendChild(s);

// let button = document.createElement("button");
// button.setAttribute("id", "load-button");
// button.innerHTML = "Lets Visearch";

// document.body.appendChild(button);

//把按钮也搬到浮动右侧
let button = document.createElement("button");
button.innerHTML = "Show SVG";
button.style.cssText = "position: fixed; top: 0; right: 0; z-index: 999; width: 100px; height: 50px; background-color: green; color: white; font-size: 20px;";

button.addEventListener("click", function () {
    var div = d3.select("body").append("div")
        .style("position", "fixed")
        .style("top", "50px")
        .style("right", "0")
        .style("z-index", "999")
        // .style("background-color", "rgba(255, 255, 255, 0.5)")
        .style("background-color", "rgba(0, 0, 0, 0.5)")
        .style("padding", "20px")
        .style("width", "40%")
        .style("height", "80%")
        .style("overflow", "auto")
        .style("border-radius", "10px")
        .style("box-shadow", "0 0 10px #ccc")
        .on("click", function () {
            d3.select(this).style("display", "none");
        });
    // var svg;
    // div.append(svg.node())
});
document.body.appendChild(button);



// window.addEventListener('load', function() {   //是否可以listen别的
// window.addEventListener('scroll', function() {  //改成滚动的时候就触发?或者滚动多次
// window.addEventListener('scroll', function() {
//   if (document.body.scrollHeight == document.body.scrollTop + window.innerHeight) {

//按键响应
button.addEventListener("click", function () {


    //相当于初始化d3.js中的d3.select(...)函数可以用来选择页面中的元素,而.remove()方法可以用来删除选中的元素。因此在重新渲染之前,可以使用这些方法来删除之前渲染的元素。
    d3.selectAll("svg > *").remove();

    console.log("lets ViSearch click");


    //无按钮的添加一层
    var svg = d3.select("body").append("svg")
        .attr("width", "50%")
        .attr("height", "100%")
        .style("position", "fixed")
        .style("top", "0")
        .style("right", "0")
        .style("z-index", "999")



    var simulation = d3.forceSimulation()
        //key makethe link follow id as indicator to find nodes
        .force("link", d3.forceLink().id(function (d) {
            return d.id;

        }))
        .force("charge", d3.forceManyBody())
        //centre setting up
        .force("center", d3.forceCenter(width / 2, height / 2));

    //试着改变力图的nodes吸引力和排斥力
    simulation.alphaDecay(0.05) // 衰减系数,值越大,图表稳定越快
    simulation.force('charge')
        .strength(-forceRate) // 排斥力强度,正值相互吸引,负值相互排斥
    simulation.force('link')
        .id(d => d.id) // set id getter
        .distance(100) // 连接距离
        .strength(1) // 连接力强度 0 ~ 1
        .iterations(1) // 迭代次数

    // //定义tip
    // var tip = d3.tip()
    //     .attr('class', 'd3-tip')
    //     .style("z-index", "9999")
    //     .offset([-10, 0])
    //     .html(function (d) {
    //         return "<a href='" + d.url + "' target='_blank'>" + d.name + "</a>";
    //         // return "<a href='" + d.url + "' target='_blank'>" + d.year + "-" + d.month + "  " + d.name + "</a>";
    //     })
    // svg.call(tip);


    //added a var
    var dragging = false;

    // dragXXX related
    function dragstarted(d) {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
        dragging = true;

    }

    function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
    }

    function dragended(d) {
        //            alphaTarget(num)是啥意思啊,d里面的fx,fy是下一刻的x,y吗?
        if (!d3.event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
        dragging = false;
    }

    // todo #23 make the function above defined ahead ! As the userscript migration notice! https://github.dev/new4u/ViSearch/blob/4687c34a92b76ef9940162177606f2838aede93f/ViSearch.user.js#L295-L317


    //    <!--    loading data-->
    let data = {};
    var graph = {};
    // d3.json("new.json", function (error, data) {
    // d3.json("nodesAndLinks.json", function (error, data) {
    // if (error) throw error;

    // 不需要“”括起来,但是最后要用分号;

    //***开始解析网页
    /* 谷歌获取 */

    //成功在page之外获取到页面元素内容,发现百度的下一页,就是
    let elements = Array.from(document.querySelectorAll(".g"));
    // console.log(elements);
    //读取数组里内容map为value
    let dataPage = elements.map(element => {
        // console.log(element);

        //搜索到文章的标题
        let title = element.querySelector(".LC20lb");
        (title !== null) ? title = title.innerText : title = null;

        // console.log(title);

        //搜索到文章的url
        let url = element.querySelector("a");
        (url !== null) ? url = url.href : url = null;

        // console.log(url);

        //搜索到的文章的来源网站r.split(" - ")[1]
        let siteName = title;
        (siteName !== null) ? siteName = siteName.split(" - ")[1] : siteName = null;
        // console.log(siteName);

        //搜索到的文章的发布日期
        // let time = element.querySelector(".c-abstract>.newTimeFactor_before_abs"); //之前的query
        let time = element.querySelector(".MUxGbd.wuQ4Ob.WZ8Tjf > span");
        (time !== null) ? time = time.innerText : time = null; //google几天前时间可以计算一下

        // console.log(time);

        //搜索到的文章的摘要
        let abstract = element.querySelector(".VwiC3b.yXK7lf.MUxGbd.yDYNvb.lyLwlc.lEBKkf span:nth-child(2)");
        (abstract !== null) ? abstract = abstract.innerText : abstract = null;
        // console.log("abstract摘要:",abstract);

        // console.log(abstract);

        // 搜索到文章的关键词(relaited to经过百度分词的搜索框内容)
        let keyWords = Array.from(element.querySelectorAll(".qkunPe"));
        (keyWords !== null) ? keyWords = keyWords.map(item => {
            return item.innerText
        }) : keyWords = null;
        // console.log(keyWords);


        let elementEach = {
            title,
            url,
            siteName,
            time,
            abstract,
            keyWords
        };



        // console.log(elementEach);
        return elementEach

    });

    console.log(dataPage);
    // let dataPageString = JSON.stringify(resultPages);
    // let dataPage = JSON.parse(dataPageString);

    // console.log("dataPage:"+dataPage);

    let keyWords = []
    for (let i in dataPage) {
        let temp = dataPage[i].keyWords;
        keyWords = keyWords.concat(temp)
    }
    let keyWordsSet = Array.from(new Set(keyWords))


    //将nodes,links写成json,到d3里面读
    // 尝试声明一个类

    let nodes = []
    let links = []
    let id = 0

    //4,san,id=40000,49999
    // san
    //   id
    //     自增长
    //   name
    //     dataPage.title
    //   value
    //     id
    // dataPage.abstract
    //   origin
    //     baidu
    //   time
    //     dataPage.time
    //       可以控制颜色
    //       year
    //   url
    //     dataPage.url
    //   links
    //     source:
    //     link
    //      都是目的地之前都指向了
    let sanidstart = id;
    let sanNode = []

    for (let j = 0; j < dataPage.length; j++) {
        sanNode.push(
            {
                category: 4,
                id: "san" + j,
                name: dataPage[j].title,
                value: dataPage[j].abstract,
                origin: dataPage[j].siteName,
                time: dataPage[j].time,
                //    提取前四个数字做年.replace(/[^0-9]/ig,"").substr(0,4)
                // year: (dataPage[j].time !== null && (dataPage[j].time).replace(/[^0-9]/ig, "") !== "") ? (dataPage[j].time).replace(/[^0-9]/ig, "").substr(0, 4) : null,
                year: (dataPage[j].time !== null && (dataPage[j].time).replace(/[^0-9\u4e00-\u9fa5]/ig, "") !== "") ? (dataPage[j].time).replace(/[^0-9\u4e00-\u9fa5]/ig, "").substr(0, 4) : null,
                url: dataPage[j].url,
                //为了前面能找到,再加一条,也可以用这个循环,就少了一个!,这样资料全一些
                keyWords: dataPage[j].keyWords,
                type: "san",
            })
    }
    ;

    let keyidstart = id
    //3,key,id=30000-399993
    //   key
    //     id
    //       自增长
    //     name
    //       四个字以下的keywords
    //     value
    //       id
    //     origin
    //       null
    //     time
    //       null
    //     url
    //       null
    //     links
    //       source:本身
    //       target:
    //         关联的san
    let keyNode = []
    for (let i = 0; i < keyWordsSet.length; i++) {

        if (keyWordsSet[i].length <= 4) {
            keyNode.push({
                category: 3,
                id: "key" + i,
                name: keyWordsSet[i],
                value: 30000 + i + "",
                type: "key",
            })
            //     !!! link 如果 keyNode.name 在dataPage.keywords li indexof 就连接(先写san)
            //    for (k)
            for (let j = 0; j < sanNode.length; j++) {
                //keywords是数组,不知道行不行,可以
                if (sanNode[j].keyWords.indexOf(keyWordsSet[i]) !== -1) {
                    links.push({
                        source: "key" + i,
                        target: "san" + j,
                        value: 1

                    })
                }
            }


        }
    }

    let tagid = id
    //2,tag,id=20000-29999,tag就是长度大于4的keywordsSet
    let tagNode = [];
    for (let i = 0; i < keyWordsSet.length; i++) {
        if (keyWordsSet[i].length > 4) {
            tagNode.push({
                category: 2,
                id: "tag" + i,
                type: "tag",
                name: keyWordsSet[i],
                value: id,
            })
            for (let j = 0; j < sanNode.length; j++) {
                //keywords是数组,不知道行不行,可以
                if (sanNode[j].keyWords.indexOf(keyWordsSet[i]) !== -1) {
                    links.push({
                        source: "tag" + i,
                        target: "san" + j,
                        value: 1

                    })
                }
            }

            for (let j = 0; j < keyNode.length; j++) {
                //如果tagNode.name包含了keyNode.name(indexof),就连一条线
                if (keyWordsSet[i].indexOf(keyNode[j].name) !== -1) {
                    links.push({
                        source: "tag" + i,
                        target: keyNode[j].id,
                        value: 1
                    }
                    )

                }
            }
        }
    }




    //1,id=10000,不能用10000+"",,只能用i+"".直接"10000"就好了
    let newsNode = {
        category: 1,
        id: "news",
        //searchtext在前面定义前面获取
        name: searchtext,
        value: id,
        type: "news"
    }
    //和key和tag都建立连接
    for (let i = 0; i < keyNode.length; i++) {
        links.push({
            source: "news",
            target: keyNode[i].id,
            value: 1
        });
    }
    for (let i = 0; i < tagNode.length; i++) {
        links.push({
            source: "news",
            target: tagNode[i].id,
            value: 1
        });


    }


    //    合并4个[],用concat(),
    nodes = nodes.concat(newsNode).concat(tagNode).concat(keyNode).concat(sanNode);
    // console.log("nodes", nodes, "links", links);
    data = { 'nodes': nodes, 'links': links };
    console.log(data)






    graph = data;
    // console.log(data);
    //d3 mapping data to html,make line with = 1
    //    links
    var link = svg.append("g")
        .attr("class", "links")
        .selectAll("line")
        .data(graph.links)
        .enter()
        .append("line")
        .attr("stroke-width", function (d) {
            return d.value;
        });
    //   nodes
    var node = svg.append("g")
        .selectAll("circle")
        .data(graph.nodes)
        .enter()
        .append("circle")
        //        试着给每种type加一个class,要放在数据读取之后
        .attr("class", function (d) {
            // return "nodes";
            return "nodes " + d.type;
        })
        .attr("r", function (d) {
            //    make radius of node circle
            return sizes[d.category - 1];                    // return r;
        })
        .attr("fill", function (d) {
            //    配合现在category从1开始,今后可以重新设计一下category make color of node circle,也可以加到后面,统一修改
            // console.log(d.category);
            return colors[d.category - 1];

        })
        .attr("stroke", "none")
        .attr("name", function (d) {
            //    make text of node
            return d.name;
        })

        // set drag event
        .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended));

    //固定中心文章位置,用class来控制
    //固定中心文章位置,fx可以设置哈哈,或者大面积的可以用tick,详见https://stackoverflow.com/questions/10392505/fix-node-position-in-d3-force-directed-layout,实验证明,还可以用type属性来控制fx,fy

    svg.select(".news")
        .attr("fx", function (d) {

            return d.fx = width / 2;
        })
        .attr("fy", function (d) {
            return d.fy = height / 2;
        })
    // .attr("r", function (d) {
    //     return 2 * r;
    // });
    // .call(force.drag);

    //定位关键词(new+tag)

    // var screenratio = width / height;

    // /**
    //      * 获取坐标数组
    //      * r 圆的半径
    //      * ratio 压缩比例
    //      * size 等分值 需要将圆划分为多少份d3.selectAll(".san")._groups[0].length
    //      */
    // function getXYS(r, ratio, type) {
    //     var xys = [];
    //     var typeString = "." + type;
    //     var size = d3.selectAll(typeString.toString())._groups[0].length
    //     var r = r * size * ratio;
    //     for (var i = 0; i < size; i++) {
    //         //圆心坐标
    //         var x = width / 2, y = height / 2;
    //         // 计算弧度
    //         var rad = i * 2 * Math.PI / size;

    //         // r*Math.cos(rad) 弧线的终点相对dot的水平偏移
    //         // r*Math.sin(rad) 弧线的终点相对dot的垂直偏移
    //         // compressionRatio 垂直压缩比例
    //         // 定义了x_,y_
    //         let x_ = ratio * r * Math.sin(rad) + x;
    //         let y_ = -r * Math.cos(rad) + y;
    //         xys.push({
    //             px: x_,
    //             py: y_
    //         });
    //     }

    //     return xys;
    // }

    // //暂且将各自的个数(tag和key)用links.length代替,R=输入的r*size(个数)
    // var tagXys = getXYS(sizes[2], 1, "tag"); //段落

    // var keyXys = getXYS(sizes[3], width / height, "key"); //关键字

    // var sanXys = getXYS(sizes[4], width / height, "san"); //关键字

    // //固定段落位置
    // svg.selectAll(".tag")
    //     .attr("type", function (d, i) {
    //         var obj = tagXys[i]
    //         d.fx = obj.px;
    //         d.fy = obj.py;

    //     });

    // //固定关键字位置
    // svg.selectAll(".key").attr("type", function (d, i) {
    //     var obj = keyXys[i]
    //     d.fx = obj.px;
    //     d.fy = obj.py;
    // });

    //san文章点击, 没有name这个
    //存放三度文章size
    var sansize = [];

    var year = 0;
    var year_index = 0;
    //计算连接数
    svg.selectAll(".san")
        .attr("r", function (d) {
            let uniqueWords = new Set(d.keyWords);
            let radius = uniqueWords.size * 10;
            console.log("radius:", radius);
            return radius
        })
        .style("fill-opacity", 0.5);




    // var sansortsizeIndex = [];
    // var sansortsize = { min: 1, mid: 1, max: 1 };
    // var minSize = d3.min(sansize); //最小值
    // var maxSize = d3.max(sansize);
    // //判断最小值
    // var min_index = 0;
    // var mid_index = 0;
    // var max_index = 0;
    // for (var i in sansize) {
    //     if (sansize[i] <= (minSize + (maxSize - minSize) * 0.5)) { //50%以下在外圈
    //         sansortsizeIndex.push({ type: 'min', index: min_index });
    //         sansortsize.min += 1;
    //         min_index++;
    //     } else if (sansize[i] >= (maxSize - (maxSize - minSize) * 0.2)) { //20%以上在里圈
    //         sansortsizeIndex.push({ type: 'max', index: max_index });
    //         sansortsize.max += 1;
    //         max_index++;
    //     } else {
    //         sansortsizeIndex.push({ type: 'mid', index: mid_index });
    //         sansortsize.mid += 1;
    //         mid_index++;
    //     }
    // }
    //     function getXYS3(r, ratio, size) {
    //         var xys3 = [];

    //         for (var i = 0; i < size; i++) {
    //             //圆心坐标
    //             var x = width / 2, y = height / 2;
    //             // 计算弧度
    //             var rad = i * 2 * Math.PI / size + Math.PI / 2;

    //             // r*Math.cos(rad) 弧线的终点相对dot的水平偏移
    //             // r*Math.sin(rad) 弧线的终点相对dot的垂直偏移
    //             // compressionRatio 垂直压缩比例
    //             x_ = ratio * r * Math.sin(rad) + x;
    //             y_ = -r * Math.cos(rad) + y;
    //             xys3.push({
    //                 px: x_,
    //                 py: y_
    //             });
    //         }

    //         return xys3;
    //     }
    // var sanXys = {};
    // sanXys.min = getXYS3((height / 2 - 150) / 3 * 3 + 100, width / height, sansortsize['min']);
    // sanXys.mid = getXYS3((height / 2 - 150) / 3 * 2 + 100, width / height, sansortsize['mid']);
    // sanXys.max = getXYS3((height / 2 - 150) / 3 * 1 + 100, width / height, sansortsize['max']);



    //如果url不为空,把node的半径扩充成原来的三倍
    // svg.selectAll(".san")
    // .attr("r", function(d) {
    //     if (d.url) {
    //         return d3.select(this).attr("r") * 3;
    //     } else {
    //         return d3.select(this).attr("r");
    //     }
    // });

    // //san文章,GPT学习.tag的固定段落位置, 将下列代码补全,将.san的node根据data.time进行布局, time接近现在的较靠屏幕上方(time数据示例:time: "23小时前" 或者 time: "2023年1月8日 "), 同时将time为空的通通放在graph的底部
    // var now = new Date();
    // var sanNodes = svg.selectAll(".san")
    // .attr("type", function (d, i) {
    // var obj = sanXys[i]
    // // 计算时间差
    // var time = d.time;
    // var diff;
    // if (time.indexOf("小时前") != -1) {
    // diff = -time.split("小时前")[0];
    // } else if (time.indexOf("年") != -1) {
    // var date = new Date(time);
    // diff = (now - date) / 1000 / 60 / 60 / 24;
    // } else {
    // diff = 0;
    // }
    // console.log(now,data,diff)
    // // 设置y坐标
    // var linear = d3.scaleLinear()
    // .domain([-30, 0])
    // .range([0, height - 20]);
    // d.fy = linear(diff);
    // });

    // console.log("now 是当前时间的时间戳,data 是节点的数据,diff 是时间差,表示距离当前时间的天数,负数表示过去的天数,正数表示未来的天数。")
    // // 将time为空的节点放在底部
    // sanNodes.filter(function (d) { return !d.time; }).attr("fy", height);

    // //调用tips
    // node.on("mouseover", tip.show)
    //     .on("mouseout", tip.hide)





    //    显示所有的文本...
    var text = svg.append("g")
        .attr("class", "texts")
        .selectAll("text")
        .data(graph.nodes)
        .enter()
        .append("text").attr("font-size", function (d) {
            // return d.size;
            let uniqueWords = new Set(d.keyWords);
            let radius = uniqueWords.size * 2;
            let fontSize = radius * sizes[d.category - 1];

            console.log("d:", d, ";font-size return", fontSize)
            return fontSize;
        })
        .attr("fill", function (d) {
            // return "red";
            return colors[d.category - 1];
        })
        .attr("name", function (d) {
            return d.time;
        })
        .text(function (d) {
            return (d.time ? d.time + d.name : d.name);
        })
        .attr("text-anchor", "center")
        .on("click", function (d) {
            if (d.url) {
                window.open(d.url, "_blank");
            }
        })
        .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended)
        );

    //圆增加title...
    node.append("title").text(function (d) {
        return d.time + d.name ;
    })

    //    simulation里面的ticked初始化生成图形
    simulation
        .nodes(graph.nodes)
        .on("tick", ticked);
    simulation.force("link")
        .links(graph.links);

    /* //点击任意一个node, 不与之相连的节点和连线都变透明,怎么做

    可以在点击node事件处理函数中通过改变对应元素的透明度实现:

    获取点击的node的相邻节点
    对于不相邻的节点,修改其透明度
    对于不相邻的连线,修改其透明度
    代码示例: */

    var isTransparent = false;

    // 为每个node绑定点击事件
    node.on("click", function(d) {
        // 根据当前状态进行相应的操作
        if (!isTransparent) {
            link.style("opacity", function(l) {
                if (d === l.source || d === l.target) {
                    return 1;
                } else {
                    return 0.1;
                }
            });
            node.style("opacity", function(n) {
                // 只对与点击的圆圈不相关的圆圈透明度进行更改
                var linked = false;
                link.each(function(l) {
                    if (d === l.source || d === l.target) {
                        linked = true;
                        return;
                    }
                });
                if (!linked) {
                    return 0.1;
                } else {
                    return 1;
                }
            });
            isTransparent = true;
        } else {
            link.style("opacity", 1);
            node.style("opacity", 1);
            isTransparent = false;
        }
    });





    // ticked()函数确定link的起始点坐标(source(x1,y1),target(x2,y2)),node确定中心点(cx,cy),文本通过translate平移变化
    function ticked() {
        link
            .attr("x1", function (d) {
                return d.source.x;
            })
            .attr("y1", function (d) {
                return d.source.y;
            })
            .attr("x2", function (d) {
                return d.target.x;
            })
            .attr("y2", function (d) {
                return d.target.y;
            })

        node
            .attr("cx", function (d) {
                return d.x;
            })
            .attr("cy", function (d) {
                return d.y;
            })
        text.attr('transform', function (d) {
            // return 'translate(' + d.x + ',' + (d.y + d.size / 2) + ')';
            return 'translate(' + d.x + ',' + (d.y + sizes[d.category - 1] / 2) + ')';
        });
    }



    // $('#mode span').click(function (event) {
    //     //     //    span all set to inactive state
    //     //     $('#mode span').removeClass('active');
    //     //     //    the clicked span activaed
    //     //     $(this).addClass('active');
    //     //     //    text hide nodes display
    //     //     if ($(this).text() === '节点') {
    //     //         $('.texts' && 'text').hide();
    //     //         $('.nodes' && 'circle').show();
    //     //     } else {
    // $('texts' && 'text').show();
    // $('.nodes' && 'circle').hide();
    //     //     }
    //     // })

    //暂时可以使用注释以下两行代码显示文字,或者circle
    // $('texts' && 'text').show();
    // $('.nodes' && 'circle').hide();

    //     //搜索框中输入内容则响应该事件
    //     // keyup按键敲击响应event
    //     $('#search1 input').keyup(function (event) {
    //         console.log($(this).val());
    //         if ($(this).val() == '') {
    //             d3.select('#svg .texts').selectAll('text').attr('class', '');
    //             d3.select('#svg .nodes').selectAll('circle').attr('class', '');
    //             d3.select('#svg .links').selectAll('line').attr('class', '');
    //         } else {
    //             var name = $(this).val();
    //             d3.select('#svg .nodes').selectAll('circle').attr('class', function (d) {
    //                 if (d.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) {
    //                     return '';
    //                 } else {
    //                     return 'inactive';
    //                 }
    //             });
    //             d3.select('#svg .texts').selectAll('text').attr('class', function (d) {
    //                 if (d.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) {
    //                     return '';
    //                 } else {
    //                     return 'inactive';
    //                 }
    //             });
    //             d3.select("#svg .links").selectAll('line').attr('class', function (d) {
    //                 return 'inactive';
    //             });
    //         }
    //     });
    // });
    // });
});