Bing 时光机

随意跳转Bing不同时间搜索结果

As of 2023-04-19. See the latest version.

// ==UserScript==
// @name         Bing 时光机
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  随意跳转Bing不同时间搜索结果
// @author       hzhbest
// @match        https://*.bing.com/search?*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=bing.com
// @grant        none
// @license      GNU GPLv3 
// ==/UserScript==

(function() {
    'use strict';

    // 根据 location.href 识别日期区间搜索行为
	// https://www.bing.com/search?q=AAAA&filters=ex1%3a%22ez5_18993_19052%22
	// 其中,“q=”后面是关键词,“filters=”后面是结果过滤,“ex1%3a%22ez5……%22”是日期区间的标识
	// 识别搜索关键词 kw 和日期区间始末点 qiri 和 vsri
	// 构建时光机框【divMain【labelTitle】【labelStart】【inputStart】【labelEnd】【inputEnd】【divBtnGo】】
	// 转换 qiri 和 vsri 为日期格式(yyyy-m-d)文本 qistr 和 vsstr 并放入 inputStart 和 inputEnd 中
	// 点 divBtnGo 时,补全两 input* 中的 *str 为日期格式,并转换为日期数 *ri,与 kw 重组新网址,并当前页面加载

	setTimeout(init,2000);
	const d0 = Date.parse('1970-1-1');
	const msxd = 24*60*60*1000;
	let divMain = document.body.appendChild(document.createElement('div'));
	divMain.id = 'TM_Main';

	function init(){
		let kw, qiri, vsri, arri;
		let lTitle, lKword, inKword, lStart, lEnd, inStart, inEnd, divBtnGo;

		lTitle = creaElemIn('label', divMain);
		lKword = creaElemIn('label', divMain);
		inKword = creaElemIn('input', divMain);
		lStart = creaElemIn('label', divMain);
		inStart = creaElemIn('input', divMain);
		lEnd = creaElemIn('label', divMain);
		inEnd = creaElemIn('input', divMain);
		divBtnGo = creaElemIn('div', divMain);

		lTitle.innerHTML = "Bing时光机";
		lKword.innerHTML = "搜索关键词";
		lStart.innerHTML = "开始日期";
		lEnd.innerHTML = "结束日期";
		divBtnGo.innerHTML = "立即穿梭";
		inKword.title = "在此输入关键词,跟随时光机穿梭!";
		inStart.title = "开始日期,可只输年份或年月,可用鼠标滚轮增减月份";
		inEnd.title = "结束日期,可只输年份或年月,可用鼠标滚轮增减月份";

		inKword.id = 'TM_kword';
		inStart.id = 'TM_start';
		inEnd.id = 'TM_end';
		divMain.className = 'TM_Main';
		lTitle.className = 'TM_Title';
		lKword.className = lStart.className = lEnd.className = 'TM_Label';
		inKword.className = inStart.className = inEnd.className = 'TM_Input';
		inStart.addEventListener('DOMMouseScroll', changemonth);
		inEnd.addEventListener('DOMMouseScroll', changemonth);
		inStart.addEventListener('keypress', submit);
		inEnd.addEventListener('keypress', submit);
		inKword.addEventListener('keypress', submit);
		divBtnGo.className = 'TM_Button';
		divBtnGo.addEventListener('click', go);

		kw = getkw(location.href);
		inKword.value = decodeURI(kw);

		arri = getds(location.href);console.log(arri);
		qiri = arri[0];
		vsri = arri[1];
		let edate, sdate;
		if (qiri == 0 || vsri == 0) {
			edate = new Date();
			sdate = new Date(edate - 180 * msxd);
		} else {
			edate = numTOdate(vsri);
			sdate = numTOdate(qiri);
		}
		inEnd.value = edate.getFullYear() + "-" + (edate.getMonth() + 1) + "-" + edate.getDate();
		inStart.value = sdate.getFullYear() + "-" + (sdate.getMonth() + 1) + "-" + sdate.getDate();
	}

	function go(){	//点“穿梭”按钮后以时光机内的关键词和日期进行跳转(若关键词为空则使用当前关键词)
		let inS = document.querySelector('#TM_start');
		let inE = document.querySelector('#TM_end');
		let inK = document.querySelector('#TM_kword');
		let ednum, sdnum;
		ednum = dateTOnum(validdate(inE.value,true));//console.log(ednum);
		sdnum = dateTOnum(validdate(inS.value,false));//console.log(sdnum);
		let kw = inK.value || getkw(location.href);
		window.location.href = "https://www.bing.com/search?q=" + kw + "&filters=ex1%3a%22ez5_" + sdnum + "_" + ednum + "%22";
	}

	function changemonth(e){
		e.preventDefault();
		e.stopPropagation();
		let islast = (this.id == 'TM_end')? true : false;
		let cdate = new Date(validdate(this.value, islast));
		let ddate = new Date(cdate.getFullYear(), (cdate.getMonth() + ((e.detail > 0)? 1 : -1)), cdate.getDate());
		this.value = ddate.getFullYear() + "-" + (ddate.getMonth() + 1) + "-" + ddate.getDate();
	}

	function submit(e){
		if (e.which == 10 || e.which == 13) {
			go();
		}
	}

    let csstxt = `
        .TM_Main {position: fixed; top: 100px; left: 0px; z-index: 100000; background: #fff;
            padding: 4px; border: 1px solid #ddd; font-size: 14pt; width: 115px; overflow: hidden;
            transition: width 0.2s linear 3s;}
        .TM_Main:hover {width: 190px; transition: width 0.2s linear 0s; box-shadow: 0 1px 12px 4px #ccc;}
        .TM_Title {font-size: 15pt; font-weight: bold;}
        .TM_Label {overflow: hidden; white-space: nowrap; display: block;color: #969696; padding-top: 9px;}
        .TM_Input {width: 180px; font-size: larger; border-bottom: 1px solid #cecece;
            border-top: none; border-left: none; border-right: none; margin-bottom: 1px;}
        .TM_Input:hover {border-bottom: 2px solid #878787; margin-bottom: 0px;}
        .TM_Button {margin:5px; padding: 0px 5px; width: 50px; height: 50px;
            cursor: default; position: absolute; left: 120px; bottom: 3px;
            text-align: center; background: #f5f5f5;}
        .TM_Button:hover {outline: 2px solid #99e; background: #eef; color: black;}
        .TM_Button:active {outline: 2px solid #22f; background: #cce;}
    `;
    addCSS(csstxt);

	// https://cn.bing.com/search?q=js&filters=ex1%3a%22ez5_19327_19359%22
	function getkw(url){	//从url中提取关键词
		let exp = /(?<=q\=)[^&]+(?=&?)/i;
		return exp.exec(url)[0];
	}

	function getds(url){	//从url中提取日期数数组[qiri,vsri]
		let exp = /(?<=filters\=ex1\%3a\%22ez5_)[\d_\.]+(?=\%22)/i;	//不知为啥有时候出的日期会带小数点……
		let ds = exp.exec(url);console.log(ds);
		if (!!ds) {
			return ds[0].split("_");	//正则匹配出的是结果的“数组”,因此第一匹配结果需要是数组index 0的元素
		} else {
			return "0_0".split("_");
		}
	}

	function validdate(str,islast){		//将文本补全为“yyyy-m-d”格式,并输出日期,其中要补全的文本必须有四位年,可缺日或缺月日,即“yyyy”、“yyyy-m”格式
		let odate,oy,om,od;
		let ard = str.split('-');
		if (ard.length == 1){
			om = (islast)? 11 : 0;
			od = (islast)? 31 : 1;
			odate = new Date(parseInt(str), om, od);
		} else if (ard.length == 2){
			oy = parseInt(ard[0]);
			om = parseInt(ard[1]) - 1;
			odate = (islast)? new Date(oy,om + 1, 0) : new Date(oy, om, 1);
		} else {
			odate = Date.parse(str)
		}
		return odate;
	}

	function dateTOnum(odate){	//日期转换成日数
		return (odate - d0) / msxd;
	}

	function numTOdate(onum){	//日数转换为日期对象
		return new Date(d0 + onum * msxd);
	}

	function creaElemIn(tagname, destin) {
		let theElem = destin.appendChild(document.createElement(tagname));
		return theElem;
	}

    function addCSS(css) {
        let stylenode = creaElemIn('style',document.getElementsByTagName('head')[0]);
        stylenode.textContent = css;
        stylenode.type = 'text/css';
        stylenode.id = 'ali_c_toc';
    }




})();