Greasy Fork is available in English.

SearchTimeMachine 搜索时光机

Display time filtering function in the right sidebar. 在右侧栏中显示时间过滤功能

// ==UserScript==
// @name         SearchTimeMachine 搜索时光机
// @namespace    http://tampermonkey.net/
// @version      0.0.2
// @description  Display time filtering function in the right sidebar. 在右侧栏中显示时间过滤功能
// @author       Exisi
// @license      MIT License
// @match        *.baidu.com/s*
// @match        *.google.com.*/search*
// @match        *.google.com/search*
// @match        *.google.com.hk/search*
// @match        *.bing.com/*search*
// @match        *.yahoo.com/search*
// @match        *.yandex.com/search*
// ==/UserScript==
(function () {
	"use strict";
	const data = [
		{
			name: "bing",
			timeFilterClass: [".b_dropdown div", "b_ans"],
			sideBarElement: "#b_context",
		},
		{
			name: "baidu",
			timeFilterClass: [],
			sideBarElement: "#content_right",
		},
		{
			name: "google",
			timeFilterClass: ["#hdtbMenus", "g-menu"],
			sideBarElement: "#rcnt>div:not(#center_col)",
			container: "#rcnt",
		},
		{
			name: "yahoo",
			timeFilterClass: ["#horizontal-bar .last .timefilter-list"],
			sideBarElement: "#right",
		},
		{
			name: "yandex",
			timeFilterClass: [],
			sideBarElement: "#search-result-aside",
		},
	];
	const url = window.location.href;
	const urlParams = new URLSearchParams(window.location.search);
	const type = data.findIndex((item) => url.includes(item.name));

	let filterItem = document.querySelector(data[type].timeFilterClass[0]);
	let sidebar = document.querySelector(data[type].sideBarElement);

	const style = document.createElement("style");
	style.innerHTML = `.form-footer button:hover{ background-color: #f5f5f6 !important;}`;
	document.head.appendChild(style);

	const customRangeFilter = `<div style="margin-top: 10px;font-size: 13px; border-top: 1px solid #ccc;">
		<div style="margin: 10px 0;">自定义范围</div>
		<form id="customDateFilter">
			<div class="form-container" style="display:flex;">
				<input class="filter-date-start" type="date"
					style="border-radius: 2px;border: 1px solid #ddd; line-height: 16px; padding: 5px;" />
				<input class="filter-date-end" type="date"
					style="margin-left: 10px; border-radius: 2px;border: 1px solid #ddd; line-height: 16px; padding: 5px;" />
			</div>
			<div class="form-footer" style="margin: 10px 0; display: flex; justify-content: space-between; align-items: center">
				<span class="validateMessage" style="visibility: hidden; color:red;font-size:12px;">*日期不合法</span>
				<button style="cursor:pointer; padding: 0 10px; min-height: 32px; background-color: #FAFAF9; color: #111; border-radius: 2px; border: 1px solid #ddd;">应用</button>
			</div>
		</form>
	</div>`;

	if (data[type].name == "bing") {
		const cloneFilterItem = filterItem.cloneNode(true);
		cloneFilterItem.style.padding = 0;
		cloneFilterItem.querySelectorAll("a").forEach((item) => {
			item.style.color = "#666";
			item.style.borderRadius = "5px";
			item.addEventListener("mouseover", () => {
				item.style.backgroundColor = "#f5f5f5";
			});
			item.addEventListener("mouseout", () => {
				item.style.backgroundColor = "#ffffff";
			});
		});

		const timeFilterItem = document.createElement("li");
		timeFilterItem.appendChild(cloneFilterItem);
		timeFilterItem.className = data[type].timeFilterClass[1] + " sidebar-filter";
		timeFilterItem.style.marginBottom = "35px";
		timeFilterItem.style.borderRadius = "6px";
		timeFilterItem.style.marginTop = "5px";
		timeFilterItem.style.padding = "10px 20px 5px 20px";
		timeFilterItem.style.border = "1px solid #ddd";

		sidebar.prepend(timeFilterItem);

		const customDateFilter = document.createElement("div");
		customDateFilter.innerHTML = customRangeFilter;
		const innerFilterItem = document.querySelector(data[type].sideBarElement + " .sidebar-filter");
		innerFilterItem.appendChild(customDateFilter);

		const startDateInput = document.querySelector(".filter-date-start");
		const endDateInput = document.querySelector(".filter-date-end");

		startDateInput.addEventListener("change", () => {
			if (!validateDate(startDateInput.value)) {
				startDateInput.value = "";
				return;
			}
		});

		endDateInput.addEventListener("change", () => {
			if (!validateDate(endDateInput.value)) {
				endDateInput.value = "";
				return;
			}
		});

		const rangeFilterBtn = document.querySelector(".form-footer button");
		rangeFilterBtn.addEventListener("click", (e) => {
			e.preventDefault();
			const startDateText = startDateInput.value;
			const endDateText = endDateInput.value;

			if (!startDateText || !endDateText) {
				return;
			}

			const startDayDiff = getDiffDay(startDateText, "1970/1/1");
			const endDayDiff = getDiffDay(endDateText, "1970/1/1");

			urlParams.set("filters", `ex1:"ez5_${startDayDiff}_${endDayDiff}"`);
			window.location.search = urlParams.toString();
		});
	}

	if (data[type].name == "baidu") {
		style.innerHTML += `.sidebar-filter-list li { background-color: #ffffff; padding: 5px; list-style-type: none; cursor: pointer; } .sidebar-filter-list li:hover { background-color: #f1f4fe; color: #2440b3; }`;

		const filterList = document.createElement("ul");
		filterList.className = "sidebar-filter-list";

		const liUnlimited = document.createElement("li");
		const liOneDay = document.createElement("li");
		const liOneWeek = document.createElement("li");
		const liOneMonth = document.createElement("li");
		const liOneYear = document.createElement("li");

		liUnlimited.textContent = "时间不限";
		liOneDay.textContent = "一天内";
		liOneWeek.textContent = "一周内";
		liOneMonth.textContent = "一月内";
		liOneYear.textContent = "一年内";

		liUnlimited.addEventListener("click", () => {
			urlParams.delete("gpc");
			window.location.search = urlParams.toString();
		});
		liOneDay.addEventListener("click", () => {
			const now = new Date();
			const curTimestamp = now.getTime().toString().slice(0, -3);
			const preDay = now.setDate(now.getDate() - 1);
			const preDayTimestamp = preDay.toString().slice(0, -3);

			const rangeTimeStamp = `stf=${preDayTimestamp},${curTimestamp}|stftype=1`;
			urlParams.set("gpc", rangeTimeStamp);
			window.location.search = urlParams.toString();
		});
		liOneWeek.addEventListener("click", () => {
			const now = new Date();
			const curTimestamp = now.getTime().toString().slice(0, -3);
			const preDay = now.setDate(now.getDate() - 7);
			const preDayTimestamp = preDay.toString().slice(0, -3);

			const rangeTimeStamp = `stf=${preDayTimestamp},${curTimestamp}|stftype=1`;
			urlParams.set("gpc", rangeTimeStamp);
			window.location.search = urlParams.toString();
		});
		liOneMonth.addEventListener("click", () => {
			const now = new Date();
			const curTimestamp = now.getTime().toString().slice(0, -3);
			const preMonth = now.setMonth(now.getMonth() - 1);
			const preMonthTimestamp = preMonth.toString().slice(0, -3);

			const rangeTimeStamp = `stf=${preMonthTimestamp},${curTimestamp}|stftype=1`;
			urlParams.set("gpc", rangeTimeStamp);
			window.location.search = urlParams.toString();
		});
		liOneYear.addEventListener("click", () => {
			const now = new Date();
			const curTimestamp = now.getTime().toString().slice(0, -3);
			const preYear = now.setFullYear(now.getFullYear() - 1);
			const preYearTimestamp = preYear.toString().slice(0, -3);

			const rangeTimeStamp = `stf=${preYearTimestamp},${curTimestamp}|stftype=1`;
			urlParams.set("gpc", rangeTimeStamp);
			window.location.search = urlParams.toString();
		});

		filterList.appendChild(liUnlimited);
		filterList.appendChild(liOneDay);
		filterList.appendChild(liOneWeek);
		filterList.appendChild(liOneMonth);
		filterList.appendChild(liOneYear);

		const customDateFilter = document.createElement("div");
		customDateFilter.innerHTML = customRangeFilter;

		const filterSidebarItem = document.createElement("div");
		filterSidebarItem.style.marginBottom = "20px";
		filterSidebarItem.appendChild(filterList);
		filterSidebarItem.appendChild(customDateFilter);

		sidebar.prepend(filterSidebarItem);

		const startDateInput = document.querySelector(".filter-date-start");
		const endDateInput = document.querySelector(".filter-date-end");

		startDateInput.addEventListener("change", () => {
			if (!validateDate(startDateInput.value)) {
				startDateInput.value = "";
				return;
			}
		});

		endDateInput.addEventListener("change", () => {
			if (!validateDate(endDateInput.value)) {
				endDateInput.value = "";
				return;
			}
		});

		const rangeFilterBtn = document.querySelector(".form-footer button");
		rangeFilterBtn.addEventListener("click", (e) => {
			e.preventDefault();
			const startDateText = startDateInput.value;
			const endDateText = endDateInput.value;

			if (!startDateText || !endDateText) {
				return;
			}

			const startDateTimestamp = new Date(startDateText).getTime().toString().slice(0, -3);
			const endDateTimestamp = new Date(endDateText).getTime().toString().slice(0, -3);

			urlParams.set("gpc", `stf=${startDateTimestamp},${endDateTimestamp}|stftype=2`);
			window.location.search = urlParams.toString();
		});
	}

	if (data[type].name == "google") {
		const observer = new MutationObserver(() => {
			filterItem = document.querySelector(data[type].timeFilterClass[0]);

			if (!filterItem) {
				return;
			}

			const filterMenu = filterItem.querySelectorAll("g-menu")[1];

			if (!filterMenu) {
				return;
			}

			const cloneFilterItem = filterMenu.cloneNode(true);
			cloneFilterItem.style.border = "1px solid #dadce0";
			cloneFilterItem.style.boxSizing = "border-box";
			cloneFilterItem.style.borderRadius = "8px";
			cloneFilterItem.style.padding = "5px";
			cloneFilterItem.style.marginBottom = "15px";

			if (!sidebar) {
				sidebar = document.createElement("div");
				sidebar.style.width = "372px";
				sidebar.style.marginLeft = "76px";
				const container = document.querySelector(data[type].container);
				container.appendChild(sidebar);
			}
			sidebar.prepend(cloneFilterItem);
			observer.disconnect();
		});
		observer.observe(document.body, { childList: true, subtree: true });
	}

	if (data[type].name == "yahoo") {
		const cloneFilterItem = filterItem.cloneNode(true);
		cloneFilterItem.className = "";
		cloneFilterItem.style.backgroundColor = "#fcfbfb";
		cloneFilterItem.style.marginBottom = "15px";
		cloneFilterItem.style.padding = "5px";
		cloneFilterItem.style.border = "1px solid rgba(0,0,0,.03)";
		cloneFilterItem.style.borderRadius = "16px";

		sidebar.prepend(cloneFilterItem);
	}

	if (data[type].name == "yandex") {
		style.innerHTML += `.sidebar-filter-list{ margin:0; padding: 10px; background-color: #ffffff; border-radius: 16px; box-shadow: 0 4px 12px 0#0d234308; } .sidebar-filter-list li { background-color: #ffffff; padding: 5px; list-style-type: none; cursor: pointer; } .sidebar-filter-list li:hover { background-color: #f1f4fe; color: #2440b3; }`;

		const filterList = document.createElement("ul");
		filterList.className = "sidebar-filter-list";

		const liUnlimited = document.createElement("li");
		const liOneDay = document.createElement("li");
		const liTwoWeek = document.createElement("li");
		const liOneMonth = document.createElement("li");

		liUnlimited.textContent = "All time";
		liOneDay.textContent = "Last day";
		liTwoWeek.textContent = "Last 2 weeks";
		liOneMonth.textContent = "Last month";

		liUnlimited.addEventListener("click", () => {
			urlParams.delete("within");
			window.location.search = urlParams.toString();
		});
		liOneDay.addEventListener("click", () => {
			urlParams.set("within", "77");
			window.location.search = urlParams.toString();
		});
		liTwoWeek.addEventListener("click", () => {
			urlParams.set("within", "1");
			window.location.search = urlParams.toString();
		});
		liOneMonth.addEventListener("click", () => {
			urlParams.set("within", "2");
			window.location.search = urlParams.toString();
		});

		filterList.appendChild(liUnlimited);
		filterList.appendChild(liOneDay);
		filterList.appendChild(liTwoWeek);
		filterList.appendChild(liOneMonth);

		const filterSidebarItem = document.createElement("div");
		filterSidebarItem.style.marginBottom = "20px";
		filterSidebarItem.appendChild(filterList);

		sidebar.prepend(filterSidebarItem);
	}

	function validateDate(date) {
		const validateMesBlock = document.querySelector(".validateMessage");
		const startDateInput = document.querySelector(".filter-date-start");
		const endDateInput = document.querySelector(".filter-date-end");

		if (isNaN(Date.parse(date))) {
			validateMesBlock.innerText = "*日期不合法";
			validateMesBlock.style.visibility = "visible";
			return false;
		}

		if (startDateInput.value && endDateInput.value && startDateInput.value > endDateInput.value) {
			validateMesBlock.innerText = "*开始日期不能大于结束日期";
			validateMesBlock.style.visibility = "visible";
			return false;
		}

		validateMesBlock.style.visibility = "hidden";
		return true;
	}

	function getDiffDay(startDate, endDate) {
		const oneDay = 1000 * 60 * 60 * 24;
		const timestamp = Date.parse(startDate) - Date.parse(endDate);
		return Math.floor(timestamp / oneDay);
	}
})();