Google:多引擎同屏

在Google搜索页面同时显示多个搜索引擎的结果

// ==UserScript==
// @name         Google:多引擎同屏
// @namespace    http://userscripts.org/users/86496
// @version      2.0.0
// @description  在Google搜索页面同时显示多个搜索引擎的结果
// @author       hzhbest
// @match        https://www.google.com/*
// @icon         https://www.google.com/favicon.ico
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// @connect      www.baidu.com
// @connect      www.so.com
// @connect      www.sogou.com
// @connect      cn.bing.com
// @connect      s.weibo.com
// @connect      www.izito.com
// @connect      duckduckgo.com
// @license      MIT
// ==/UserScript==


(function () {

    if (window.top.document.location.href != window.self.document.location.href) return; // 在iframe中则不运行

    setTimeout(init, 500);                          //首次运行

    //判断并推进主进程,重新观察
    function init() {
        var thisurl = document.location.href;
        var kw = getUriParam(thisurl, 'q');
        // log('kw: ', kw);
        if (!kw) return;
        // https://www.google.com/search?q=AAAA&tbs=cdr%3A1%2Ccd_min%3A4%2F11%2F2012%2Ccd_max%3A4%2F11%2F2013
        let tbs = getUriParam(thisurl, 'tbs');
        var arri, dateset;
        if (!!tbs) arri = tbs.match(/\d{1,2}\%2F\d{1,2}\%2F\d{4}/g);  // 提取网址中的时间数组
        if (!!arri) {
            dateset = arri.map(ds => ggdateTOdate(ds)); // 有时间范围则引入到多引擎加载中
            console.log('dateset: ', dateset);
            go(kw, dateset);
        } else {
            go(kw);
        }
    }

    //主进程
    function go(kw, dateset) {

        //  ===设置开始=========================================================================

        // > 是否只在第一页显示(true / false)
        var onlyPageOne = true;

        //  **以下一行并非设置项,请勿修改! //如果只在第一页显示则在非第一页的页面直接退出
        if (onlyPageOne && getUriParam(document.location.href, 'start') > 0) return;

        // > 直接展示的搜索结果数量(个);一般小于等于10,因为一般搜索结果一页就10项
        var resultNumber = 3;

        // > 搜索结果的显示高度(像素);小于此高度的将按实际高度显示,大于此高度的在鼠标移上时展开;
        //     要想不收缩显示,将该值设得较大(例如1000)即可
        var disHeight = 120;

        // > 与主搜索结果的水平间距(像素)
        var lgap = 50;

        // > 获取搜索结果最长尝试(超时)时间(秒)。
        var resultTimeout = 30;

        var xEngs = [

            // > 外部搜索引擎(提取结果信息线索)
            // 说明: xEngs[x].name - 搜索引擎的名字(用作标识)
            //		  xEngs[x].enable - 启用开关,1-启用,0-禁用
            //		  xEngs[x].boxname - 同屏结果框元素的 ID
            //		  xEngs[x].url - 搜索引擎的搜索 Url,其中“--keyword--”用于替换实际搜索关键词
            //		  xEngs[x].resultcss - 指向每条搜索结果的 css selector
            //		  xEngs[x].em - 搜索引擎对关键词强调的 css selector
            //        xEngs[x].timepara - 搜索引擎自由指定始末时间的URL参数模版
            //        xEngs[x].timeformat - 用于将时间对象转换为始末时间格式的函数
            //		  x - 显示顺序。放在前面的搜索引擎靠上显示结果

            {
                name: 'Baidu',
                enable: 1,
                boxname: "bdResult",
                url: 'https://www.baidu.com/s?wd=--keyword--',
                resultcss: '#content_left>div[class*="result"][id]:not([tpl="recommend_list"])',
                em: 'em',
                timepara: '&gpc=stf%3D--sdate--%2C--edate--%7Cstftype%3D2',
                timeformat: function (dateobj) {
                    return dateobj / 1000;
                }
            },
            {
                name: '360',
                enable: 1,
                boxname: "360Result",
                url: 'http://www.so.com/s?ie=utf-8&q=--keyword--',
                resultcss: '#main li.res-list',
                em: 'em'
            },
            {
                name: 'Bing',
                enable: 1,
                boxname: "bingResult",
                url: 'http://cn.bing.com/search?q=--keyword--',
                resultcss: 'li.b_algo',
                em: 'strong',
                timepara: '&filters=ex1%3a%22ez5_--sdate--_--edate--%22',
                timeformat: function (dateobj) {
                    const d0 = Date.parse('1970-1-1');
                    const msxd = 24 * 60 * 60 * 1000;
                    return (dateobj - d0) / msxd;
                }
            },
            {
                name: 'Sogou',
                enable: 1,
                boxname: "sogouResult",
                url: 'http://www.sogou.com/web?query=--keyword--',
                resultcss: 'div.results>div>div.struct201102',
                em: 'em'
            },
            {
                name: 'GoogleCN',
                enable: 0,
                boxname: "gcnResult",
                url: 'http://www.google.com.hk/search?q=--keyword--',
                resultcss: 'div#rso div.g',
                em: 'em',
                timepara: '&tbs=cdr%3A1%2Ccd_min%3A--sdate--%2Ccd_max%3A--edate--',
                timeformat: function (dateobj) {
                    let d = dateobj.getDate();
                    let m = dateobj.getMonth();
                    let y = dateobj.getFullYear();
                    return d + '%2F' + m + '%2F' + y;
                }
            },
            {
                name: 'Weibo',
                enable: 0,
                boxname: "weiboResult",
                url: 'https://s.weibo.com/weibo?q=--keyword--',
                resultcss: 'div#pl_feedlist_index>div>div.card-wrap'
            },
            {
                name: 'izito',
                enable: 0,
                boxname: "izitoResult",
                url: 'https://www.izito.com/search?q=--keyword--',
                resultcss: 'li.organic-results__item.organic-results-item',
                em: 'strong'
            }
        ]
        //  ===Config END | 设置结束=========================================================================


        var _ID = 'resultPlus';     //同屏结果主框架ID
        var _xID = '#' + _ID;

        var b;  //同屏结果主框架

        // 页面框架提取
        var bctn = qElem('div#rcnt');          //主搜索结果容器;该容器未载入时继续等待
        if (!bctn) { setTimeout(go, 300); return; }
        var lcol = qElem('div#center_col');       //搜索结果条目容器;以此容器位置确定插入同屏结果位置
        var lmar = (!!lcol) ? (getX(lcol) + lcol.offsetWidth + lgap) : (window.innerWidth * 0.56);    //插入同屏结果的左边缘
        var b_width = Math.min(504, window.innerWidth - lmar - 30); //同屏结果的宽度
        var wcol = lcol.offsetWidth;

        // 样式表
        //同屏结果主框架位置大小和底色
        var bxstyle = _xID + '{position:absolute; top:100px; left:' + lmar + 'px; background:white; z-index:10; width:' + b_width + 'px;'
            + ' line-height: 130%; border-bottom:1px solid #AACCFF;}';
        //分引擎框架样式
        var xestyle = _xID + ' .xe{border-top:1px solid #7799cc; padding: 1px 3px 3px 4px; '
            + 'background:linear-gradient(5deg, rgba(170, 204, 255,0.1) 0%, rgba(255, 255, 255,0.7) 100%),linear-gradient(0deg, rgba(255,255,255,0.65) 0%, rgba(127, 127, 203,0.3) 10%, rgb(170, 204, 255) 100%);}';
        //分引擎名称样式
        var exstyle = _xID + ' ._external {color: #6868b0 !important;}'
            + _xID + ' ._external:after {content:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAFVBMVEVmmcwzmcyZzP8AZswAZv////////9E6giVAAAAB3RSTlP///////8AGksDRgAAADhJREFUGFcly0ESAEAEA0Ei6/9P3sEcVB8kmrwFyni0bOeyyDpy9JTLEaOhQq7Ongf5FeMhHS/4AVnsAZubxDVmAAAAAElFTkSuQmCC");}';
        //关闭按钮样式
        var clstyle = _xID + ' .close{position: absolute; top: 0; right: 0; padding: 0 10px;}'
            + _xID + ' .close:hover{outline: 1px solid #731616; outline-offset: -1px; background-color: #F28E8E!important; color: #731616!important;}';
        //刷新链接样式
        var rfstyle = _xID + ' ._refresh{display: none; margin: 0 10px;}'
            + _xID + ' .xe:hover ._refresh{display: inline-block;}'
        //同屏搜索结果通用样式
        var glo_style = 'body{overflow: auto !important; min-height: 1200px;}'  /*不加这个在百度自己搜索结果太少时会使同屏结果显示不全 */
            + _xID + ' p, ' + _xID + ' ul {margin: 0; padding:0;}'
            + _xID + ' li{list-style: none outside none;}'
            + _xID + ' a{color: #2626A8;}'
            + _xID + ' a div>div{display: inline;}' /*链接中的图片之类*/
            + _xID + ' svg{height: 14px !important; width: 14px !important;}' /*svg图片限制大小;通常是图标*/
            + _xID + '>div>*{height: auto!important;}' /*收缩搜索结果自带高度*/
            /*同屏结果框架样式*/
            + _xID + ' div._result{max-height:' + disHeight + 'px; margin-bottom:5px; background:white; overflow:hidden; transition: height, max-height 2.5s ease-out 1s;}'
            + _xID + ' div._result:hover{max-height: 100000px; margin-bottom:0px; padding-bottom:5px; background: #F0F7F9; transition: height, max-height 0.2s ease-out; outline: 1px solid #82BDCE;}'
            + _xID + ' div._result h3, ' + _xID + ' div._result h2{font-size:13pt!important; font-weight: 800; border-bottom: 1px solid white; margin-bottom:2px;}'
            + _xID + ' div._result:hover h3, ' + _xID + ' div._result:hover h2{border-bottom: 1px solid #a7cDd6;}'
            /*更多结果框架样式*/
            + _xID + ' ._resultMore{max-height: none!important;}'
            + _xID + ' ._re_hide>div,' + _xID + ' ._no_result{display:none;}'
            + _xID + ' ._re_more{display: block; height: 20px; width: 100%; text-align: center; background: #ddd; cursor: pointer;}'
            + _xID + ' ._re_more:hover{background: #F0F7F9!important;}'
            + _xID + ' ._re_hide ._re_more{background: white;}'
            + _xID + ' div._result+div._result,' + _xID + ' ._resultMore{border-top: 1px solid #aaccff;}';
        //应付搜索结果特殊元素变样的样式
        var tablestyle = _xID + ' td{padding: 5px 0 5px 13px !important; color: #000!important; width: fit-content !important;}'
            + _xID + ' tr+tr>td{padding: 0px 0 8px 13px !important;}' + _xID + ' td>h3{margin-left: -8px; line-height: 1.3em;}';
        var li_style = _xID + ' div._result>li {padding: 5px 0 8px 13px !important; background-image: none;}'
            + _xID + ' li h3>a:first-child,' + _xID + ' li h3>em {font-size: 13pt !important; margin-left: -8px;}'
            + _xID + ' li>div, ' + _xID + ' li>p {font-size: small;}';
        //特殊结果:与主搜索结果相同
        var mat_style = _xID + ' div._match {background: #eee; background:linear-gradient(to bottom, #eee, white); max-height:1.1em;}' + _xID + ' div._match:hover{max-height: 1000px;}'
            + '._hilire{background: #ffd!important; background: linear-gradient(to bottom, #ffd, white)!important;}'
        //同屏搜索结果-引擎专属样式
        var _w;
        var gg_style = /*Google style*/`
            .xhjkHe{max-width: ${wcol}px !important;} 
            #rhs {position: fixed; left: 10px; bottom: 10px; transform-origin: bottom left; transform: scale(0.45); background: white; transition: all 0.2s 1s; max-height: 60vh; overflow-y:auto;}
            #rhs:hover {transition: transform 0.3s; transform: scale(1); box-shadow: 0 0 6px -1px #333; padding: 5px; z-index: 9999999;}
            `;
        _w = 'div[id^="bingResult_"] ';
        var bg_style = /*Bing style*/ _w + '.crch,' + _w + '.sb_tsuf{display:none!important;}'
            + _w + 'h2{margin:5px 0;}' + _w + '.inner,' + _w + '.HeroTab{display: none;}'
            + _w + 'div.tptt{background: #e9e9ffed; width: fit-content; outline: 1px solid #bac4e0; border-radius: 1em; padding: 1px 0.5em;}';
        _w = 'div[id^="sogouResult_"] ';
        var sg_style = /*Sogou style*/ _w + '>div {padding: 7px 0 8px 13px !important;background-image:none;} '
            + _w + '>div>h3 {margin-left:-8px;} '
            + _w + '.img-height{float: right;} '
            + _w + '.tit-ico {background-position: left 1px;background-repeat: no-repeat;padding-left: 20px;}'
            + _w + '.item-list{display: grid; grid-template-columns: auto auto auto auto auto auto auto auto auto;}'
            + _w + '.citeurl{position: sticky; bottom: 3px; background: #e9e9ffed; width: fit-content; outline: 1px solid #bac4e0; border-radius: 1em; padding: 1px 0.5em;}';
        _w = 'div[id^="360Result_"] ';
        var sz_style = /*360 style*/ _w + 'h3>a>img {width:16px !important; height:16px !important; float: none !important;}'
            + _w + 'img {float: right;}'
            + _w + 'li:hover div>a>img {float: none;}'
            + _w + '.g-linkinfo{position: sticky; bottom: 3px; background: #e9e9ffed; width: fit-content; outline: 1px solid #bac4e0; border-radius: 1em; padding: 1px 6px !important;}';
        _w = 'div[id^="weiboResult_"] ';
        var wb_style = /*Weibo style*/ _w + 'div.menu,' + _w + '.WB_video_h5 {display: none;} '
            + _w + 'div.media{max-height: 200px; overflow: hidden;}' + _w + 'div.media:hover{overflow: auto; scrollbar-width: thin;} '
            + _w + 'div.avator{width: fit-content; float: left;} '
            + _w + 'div.avator img{float: left; margin: 3px; height:32px; width:32px;} '
            + _w + 'img.face{height:18px !important; width:18px !important;} '
            + _w + '.card-act{position: sticky; bottom: 3px; left: 300px; background: #e9e9ffed; width: fit-content; border-top: 1px solid #bac4e0;}'
            + _w + '.woo-font--retweet{margin-left: 1em; display: inline-block; height: 16px !important; content:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGlkPSJMYXllcl8xIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA2NCA2NDsiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDY0IDY0IiB4bWw6c3BhY2U9InByZXNlcnZlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiMxMzQ1NjM7fQo8L3N0eWxlPjxnPjxnIGlkPSJJY29uLUV4dGVybmFsLUxpbmsiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDM4Mi4wMDAwMDAsIDM4MC4wMDAwMDApIj48cG9seWxpbmUgY2xhc3M9InN0MCIgaWQ9IkZpbGwtMTE4IiBwb2ludHM9Ii0zNTIuMywtMzQzLjQgLTM1NC42LC0zNDUuNyAtMzI4LjgsLTM3MS40IC0zMjYuNiwtMzY5LjIgLTM1Mi4zLC0zNDMuNCAgICAiLz48cG9seWxpbmUgY2xhc3M9InN0MCIgaWQ9IkZpbGwtMTE5IiBwb2ludHM9Ii0zMjYsLTM1NC45IC0zMjkuNCwtMzU0LjkgLTMyOS40LC0zNjguNiAtMzQzLjEsLTM2OC42IC0zNDMuMSwtMzcyIC0zMjYsLTM3MiAgICAgIC0zMjYsLTM1NC45ICAgICIvPjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0tMzM0LjYtMzI0aC0zNC4zYy0yLjgsMC01LjEtMi4zLTUuMS01LjF2LTM0LjNjMC0yLjgsMi4zLTUuMSw1LjEtNS4xaDE4Ljl2My40aC0xOC45ICAgICBjLTAuOSwwLTEuNywwLjgtMS43LDEuN3YzNC4zYzAsMC45LDAuOCwxLjcsMS43LDEuN2gzNC4zYzAuOSwwLDEuNy0wLjgsMS43LTEuN1YtMzQ4aDMuNHYxOC45Qy0zMjkuNC0zMjYuMy0zMzEuNy0zMjQtMzM0LjYtMzI0ICAgICAiIGlkPSJGaWxsLTEyMCIvPjwvZz48L2c+PC9zdmc+");} '
            + _w + '.woo-font--comment{margin-left: 1em; display: inline-block; height: 16px !important; content:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHdpZHRoPSIxMDBweCIgaGVpZ2h0PSIxMDBweCIgdmlld0JveD0iLTI0NyAzNzAuOSAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IC0yNDcgMzcwLjkgMTAwIDEwMDsiDQoJIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPGc+DQoJPHBhdGggZD0iTS0xNDcuNywzOTAuNGMtMC4zLTUuNS00LjctOS44LTEwLTkuOGgtNzguOGMtNS42LDAtMTAuMSw0LjYtMTAuMSwxMC4ydjQyLjNjMCw1LjUsNC42LDEwLjIsMTAuMSwxMC4yaDIuM3YxNC4xDQoJCWMwLDEuNSwwLjgsMi44LDIuMiwzLjRjMC42LDAuMywxLjEsMC40LDEuNywwLjRjMSwwLDEuOS0wLjQsMi42LTEuMWwxNy4zLTE2LjloNTIuOGM1LjQsMCwxMC00LjcsMTAtMTAuMnYtNDIuNkwtMTQ3LjcsMzkwLjR6DQoJCSBNLTE1Mi43LDQzMy4xYzAsMi44LTIuMyw1LjItNSw1LjJoLTU0LjhsLTE2LjcsMTYuM3YtMTYuM2gtNy4zYy0yLjcsMC01LjEtMi40LTUuMS01LjJ2LTQyLjNjMC0yLjgsMi4zLTUuMiw1LjEtNS4yaDc4LjgNCgkJYzIuNywwLDQuOCwyLjIsNSw1VjQzMy4xeiIvPg0KCTxwYXRoIGQ9Ik0tMjIyLjcsMzk5LjJoNTAuNWMxLjQsMCwyLjUtMS4xLDIuNS0yLjVsMCwwYzAtMS40LTEuMS0yLjUtMi41LTIuNWgtNTAuNWMtMS40LDAtMi41LDEuMS0yLjUsMi41bDAsMA0KCQlDLTIyNS4yLDM5OC4xLTIyNC4xLDM5OS4yLTIyMi43LDM5OS4yeiIvPg0KCTxwYXRoIGQ9Ik0tMjIyLjcsNDA4LjdoNTAuNWMxLjQsMCwyLjUtMS4xLDIuNS0yLjVsMCwwYzAtMS40LTEuMS0yLjUtMi41LTIuNWgtNTAuNWMtMS40LDAtMi41LDEuMS0yLjUsMi41bDAsMA0KCQlDLTIyNS4yLDQwNy42LTIyNC4xLDQwOC43LTIyMi43LDQwOC43eiIvPg0KCTxwYXRoIGQ9Ik0tMjIyLjcsNDE4LjJoNTAuNWMxLjQsMCwyLjUtMS4xLDIuNS0yLjVsMCwwYzAtMS40LTEuMS0yLjUtMi41LTIuNWgtNTAuNWMtMS40LDAtMi41LDEuMS0yLjUsMi41bDAsMA0KCQlDLTIyNS4yLDQxNy4xLTIyNC4xLDQxOC4yLTIyMi43LDQxOC4yeiIvPg0KCTxwYXRoIGQ9Ik0tMjIyLjcsNDI3LjdoMjkuNmMxLjQsMCwyLjUtMS4xLDIuNS0yLjVsMCwwYzAtMS40LTEuMS0yLjUtMi41LTIuNWgtMjkuNmMtMS40LDAtMi41LDEuMS0yLjUsMi41bDAsMA0KCQlDLTIyNS4yLDQyNi42LTIyNC4xLDQyNy43LTIyMi43LDQyNy43eiIvPg0KPC9nPg0KPC9zdmc+DQo=");} '
            + _w + 'button{border: 0; background: transparent; margin-left: 1em;}'
            + _w + '.card-top{float: right; margin-right: 6px;}' + _w + '.card-top h4{margin: 0;}'
            + _w + 'ul{display: grid; grid-template-columns: auto auto auto auto auto auto auto auto auto;}' + _w + 'ul>li{display: inline;} ' + _w + 'ul>li img{top:0 !important;}'
            + _w + '.thumbnail{min-height:0 !important;} ' + _w + '.card-wrap{padding: 10px;} ' + _w + '.info{float: left;}' + _w + '.info span{vertical-align: middle;}'
            + _w + '.from{width: 150px; float: right; height: 1.3em; overflow: hidden;}' + _w + '.from:hover{overflow: visible; background: #e3f5f1;}'
            + _w + '.txt{clear: both;}';


        var hl_styles = new Array();    //高亮样式数组
        var xEng_boxes = new Array();   //同屏搜索结果分引擎框架

        for (const i in xEngs) {
            if (xEngs[i].enable == 1) {
                xEngs[i].url = xEngs[i].url.replace('--keyword--', kw);        //替换搜索关键词到引擎网址
                console.log('xEngs[i].url: ', xEngs[i].url);
                if (!!xEngs[i].timepara && !!dateset) {                        // 若多引擎支持时间范围且主引擎使用了时间范围
                    let sdate = xEngs[i].timeformat(dateset[0]);
                    let edate = xEngs[i].timeformat(dateset[1]);
                    let timepara = xEngs[i].timepara.replace('--sdate--', sdate).replace('--edate--', edate);
                    xEngs[i].url += timepara;
                    xEngs[i].dateset = "[ " + dateTOymd(dateset[0]) + " - " + dateTOymd(dateset[1]) + " ] ";
                }
            }
            if (!!xEngs[i].em) {
                hl_styles[i] = _xID + ' div[id^="' + xEngs[i].resultcss + '_"] ' + xEngs[i].em;
            }   //形成高亮样式
        }
        var hili_style = hl_styles.join(',') + '{color: #CC0033 !important; background: #fffcec;}';


        // 插入 CSS
        var headID = document.getElementsByTagName("head")[0];
        var cssNode = creaElemIn('style', headID);
        cssNode.type = 'text/css';
        cssNode.id = '__xEngs';
        cssNode.innerHTML = hili_style + bxstyle + xestyle + clstyle + rfstyle + glo_style + exstyle + tablestyle + li_style + mat_style + gg_style + bg_style + sg_style + sz_style + wb_style;

        // 提取Google搜索结果
        var resls = qaElem('div#rso div.g');        //Google各条搜索结果
        var ggRes = [], ggResUrls = [], ggResNo = 0;
        // var speIDs = /imagebox_bigimages|imagebox|newsbox|videobox|blogbox/;
        for (let h = 0; h < resls.length; h++) {
            // if (speIDs.test(resls[h]).id) continue;
            var gglnk = qElem('span>a', resls[h]);
            if (!gglnk) continue;
            ggResUrls.push(gglnk.href.toLowerCase());               //提取主链接网址推入结果网址数组
            ggResNo++;
            resls[h].title = '第 ' + ggResNo + ' 结果';
            ggRes.push(resls[h]);                                   //推入结果数组
        }

        // 准备同屏搜索结果主框架
        b = creaElemIn('div', document.body);
        b.id = _ID;
        // 构建关闭按钮
        var close = creaElemIn('a', b);
        close.href = '#';
        close.className = 'close';
        close.addEventListener('click', () => {
            headID.removeChild(cssNode);
            document.body.removeChild(b);
        }, false);
        close.innerHTML = 'X';
        close.title = '关闭';

        // 按启用的多引擎构建结果框并获取搜索结果填入
        for (const i in xEngs) {
            if (xEngs[i].enable == 1) {
                xEng_boxes[i] = new Array();
                resultbox(b, xEng_boxes[i], xEngs[i], resultNumber);    //构建同屏各搜索引擎结果框架
                addresult(xEng_boxes[i], xEngs[i], resultNumber);       //插入搜索结果节点
            }
        }


        // FUNCTIONS

        // 构建一个搜索引擎结果框架
        function resultbox(root, box, engObj, rescnt) { //root:供插入引擎结果的主框架,box:供写入引擎结果的盒框架,engArr:供写入引擎标志的信息数组,rescnt:先显的结果数
            var c, d, e, f, r;
            //引擎名称框
            c = creaElemIn('div', root);
            c.className = 'xe';
            c.id = engObj.boxname;
            //引擎跳转链
            d = creaElemIn('a', c);
            addtext(d, engObj.name + ' ' + (engObj?.dateset || ''));
            d.href = engObj.url;
            d.target = '_blank';
            d.className = '_external';
            // 引擎刷新链
            r = creaElemIn('a', c);
            r.className = '_refresh';
            addtext(r, "刷新结果");
            r.addEventListener('click', () => {
                addresult(box, engObj, rescnt);
            }, false);
            //先显结果框
            for (let k = 0; k < rescnt; k++) {
                box[k] = creaElemIn('div', root);
                box[k].id = engObj.boxname + '_' + (k + 1);
                box[k].innerHTML = (k == 0) ? '正在获取结果...' : '...';
            }
            //后显结果框
            if (rescnt == 10) return;
            e = creaElemIn('div', root);
            e.className = '_resultMore _re_hide';
            e.id = engObj.boxname + 'More';
            for (let k = rescnt; k < 10; k++) {
                box[k] = creaElemIn('div', e);
                box[k].id = engObj.boxname + '_' + (k + 1);
            }
            //后显结果展开按钮
            f = creaElemIn('a', e);
            f.className = '_re_more';
            f.innerHTML = '↓展开↓';
            f.title = '展开更多结果';
            f.href = '#' + engObj.boxname;
            f.addEventListener('click', function (ev) {
                ev.preventDefault();
                var s = (this.parentNode.className == '_resultMore _re_hide');
                this.parentNode.className = (s) ? '_resultMore ' : '_resultMore _re_hide';
                this.innerHTML = (s) ? '↑收起↑' : '↓展开↓';
                this.title = (s) ? '收起更多结果' : '展开更多结果';
            }, false);
        }

        // 发送请求并转递返回数据
        function addresult(box, engObj, rescnt) { //box:供写入引擎结果的盒框架,engArr:供写入引擎标志的信息数组,rescnt:先显的结果数
            var timeout = function () {
                for (const i in box) {
                    set(box[i], (i == 0) ? '* 获取结果超时 *' : '');
                }
            };
            var errortimer = setTimeout(timeout, resultTimeout * 1000);

            var option = {
                method: "GET",
                url: engObj.url,
                onload: function (_h) {
                    clearTimeout(errortimer);
                    var _Node = document.createElement('div');
                    _Node.innerHTML = _h.responseText;              //包含整个页面的div
                    initresult(_Node, box, engObj.name, engObj.resultcss, rescnt);
                }
            }
            GM_xmlhttpRequest(option);
        }

        // Initialize results
        function initresult(_Node, box, engName, resSelector, rescnt) { //_Node:需处理的搜索结果,box:供写入引擎结果的盒框架,engName:引擎名称,resSelector:从搜索结果中提取单条搜索结果的选择器,rescnt:先显的结果数
            var _result = [], _resultLinkHref;
            var no_result_con = "* 无返回结果,请尝试直接访问 *";
            for (let i = 0; i < 10; i++) {
                _result[i] = (i == 0) ? no_result_con : '';      //初始化搜索结果临时容器
            }
            var _ns = qaElem(resSelector, _Node);               //按选择器提取的搜索结果数组
            for (let i = 0, j = 0; i < 12; i++) {                   //遍历搜索结果数组,排除特殊内容,放入临时容器,以j为索引
                var _n = _ns[i];
                //log(engName+" i "+i);
                if (_n == null) { //log(engName+" j "+j);
                    if (j <= rescnt && rescnt != 10) box[rescnt].parentNode.className = '_no_result';   //若无结果需要放入后显结果框,则包含展开按钮的后显结果鵟直接不显示
                    break;
                } else {  //log(engName+" j "+j);

                    //GCN url fix
                    if (engName == 'GoogleCN') fixgcn(_n);

                    //Google url fix
                    if (engName == 'Google') fixgl(_n);

                    //360 img flx
                    if (engName == '360') fixso(_n);

                    //Bing img url fix
                    if (engName == 'Bing') {
                        fixbmg(_n);
                        if (!_n.firstChild || (!_n.firstChild.firstChild && !_n.firstChild.nextSibling) || (_n.firstChild.localName == 'script' && !_n.firstChild.nextSibling)) continue;
                    }

                    //链接都在新标签页打开
                    var _resultLinks = _n.getElementsByTagName('a');
                    for (const o in _resultLinks) {
                        if (!!_resultLinks[o].href && /\:\/\//.test(_resultLinks[o].href)) {
                            _resultLinks[o].target = '_blank';
                        }
                    }

                    //处理图片框架
                    var _imgs = _n.getElementsByTagName('img');
                    for (const o in _imgs) {
                        if (!!_imgs[o].src && _imgs[o].src.indexOf('data:') == 0) {
                            _imgs[o].parentNode.style = 'width: fit-content; height: fit-content;';
                        }
                    }

                    //将处理后的结果放入临时容器
                    _result[j] = getoutterHTML(_n);

                    //检查同屏引擎搜索结果的主链接和主引擎搜索结果,找出同归结果
                    if (!_resultLinks[0]) continue;     //跳过无链接的结果
                    if (!!_resultLinks[0].href) {
                        _resultLinkHref = _resultLinks[0].href.toLowerCase();
                        for (const p in ggResUrls) {
                            if (_resultLinkHref == ggResUrls[p] || _resultLinkHref + '/' == ggResUrls[p]) {  //deal with bing's result url
                                box[j].className = '_match';
                                _result[j] = '同 Google 第 <b>' + (Number(p) + 1) + '</b> 结果' + _result[j];
                                if (ggRes[p].className.indexOf('_hilire') == -1) {
                                    ggRes[p].className += ' _hilire';
                                    ggRes[p].title += ';同时为 ' + engName + ' 第 ' + (j + 1) + ' 结果';
                                } else {
                                    ggRes[p].title += '及 ' + engName + ' 第 ' + (j + 1) + ' 结果';
                                }
                            }
                        }
                    }
                    j++;
                    if (j == 10) break;
                }
            }
            for (let i = 0; i < 10; i++) {      //将临时结果转入结果框
                set(box[i], _result[i]);
                if (_result[i]) box[i].className += ' _result';
            }
        }


        // 修复360懒图片
        function fixso(_resultcontent) {
            var imgs = qaElem('.so-lazyimg', _resultcontent);
            if (!imgs.length) return;
            for (const i in imgs) {
                if (!!imgs[i].dataset && !!imgs[i].dataset.isrc) imgs[i].src = imgs[i].dataset.isrc;
            }
        }

        // "修复"GoogleCN链接
        function fixgcn(_resultcontent) {
            var Links = qaElem('a', _resultcontent);
            for (const i in Links) {
                if (Links[i].href && Links[i].href.match(/^http:\/\/www\.google\..*\/url\?q=/i)) {
                    Links[i].href = Links[i].href.replace(/^http:\/\/www\.google\..*\/url\?q=(.*?)&.*/i, "$1");
                }
            }
        }

        // "修复"Google链接
        function fixgl(_resultcontent) {
            var Links = qaElem('a', _resultcontent);
            for (const i in Links) {
                if (Links[i].href && Links[i].href.indexOf('/') == 0) {
                    Links[i].href = 'https://www.google.com/' + Links[i].href.slice(1);
                }
            }
        }

        // 修复Bing图片地址
        function fixbmg(_resultcontent) {
            var Imgs = qaElem('img', _resultcontent);
            for (const i in Imgs) {
                if (Imgs[i].src && Imgs[i].src.indexOf('http://') != 0 && Imgs[i].src.indexOf('data:') != 0) {
                    Imgs[i].src = 'http://www.bing.com' + Imgs[i].src;
                }
            }
        }
    }


    // 
    /** 从url中提取名为【param】的参数的值;提取不到返回空字符
     * @param {string} surl 网址
     * @param {string} param 参数名称
     * @returns {string} 参数值
     */
    function getUriParam(surl, param) {
        var result = surl.match(new RegExp("(\\?|&)" + param + "(\\[\\])?=([^&]*)"));
        return result ? result[3] : '';
    }

    function ggdateTOdate(string) { // 4%2F11%2F2012
        return new Date(string.replaceAll("%2F", '/'));
    }

    // 将日期对象转成“YYYY-MM-DD”格式
    function dateTOymd(odate) {
        return odate.getFullYear() + "-" + twodg(odate.getMonth() + 1) + "-" + twodg(odate.getDate());
    }

    // 补足两位数字
    function twodg(s) {
        if (s.length == 1) {
            s = "0" + s;
        }
        return s;
    }

    function log() {	//debug
        var args = Array.prototype.slice.call(arguments);
        console.log.apply(console, args);
    }


    // Create and insert an element
    function creaElemIn(tagname, destin) {
        var theElem = destin.appendChild(document.createElement(tagname));
        return theElem;
    }

    // scroll node into view
    function scrTo(node) {
        if (!node) return;
        if (node.getBoundingClientRect) {
            var pos = node.getBoundingClientRect();
            /*var pos_h = node.offsetHeight;*/
            document.documentElement.scrollTop = document.body.scrollTop = pos.top + window.pageYOffset - window.innerHeight / 10;
        } else {
            node.scrollIntoView();
        }
    }

    // Set content
    function set(elem, str) {
        elem.innerHTML = str;
    }

    // Get full HTML nodes in string
    function getoutterHTML(elem) {
        var a = elem.attributes, str = "<" + elem.tagName, i = 0; for (; i < a.length; i++)
            if (a[i].specified) str += " " + a[i].name + '="' + a[i].value + '"';
        if (!canHaveChildren(elem)) return str + " />";
        return str + ">" + elem.innerHTML + "</" + elem.tagName + ">";
    }
    function canHaveChildren(elem) {
        return !/^(area|base|basefont|col|frame|hr|img|br|input|isindex|link|meta|param)$/.test(elem.tagName.toLowerCase());
    }

    // Add text node
    function addtext(obj, text) {
        var content = document.createTextNode(text);
        obj.appendChild(content);
    }

    function qElem(cssSelector, root) {
        let _r = root || document;
        return _r.querySelector(cssSelector);
    }

    // get all elements by css selector
    function qaElem(cssSelector, root) {
        let _r = root || document;
        return _r.querySelectorAll(cssSelector);
    }

    // get the accumulative offsetleft of target element
    function getX(oElement) {
        var iReturnValue = 0;
        while (oElement != null) {
            iReturnValue += oElement.offsetLeft;
            oElement = oElement.offsetParent;
        }
        return iReturnValue;
    }


})();