[SNOLAB] Alt + 123... Searching Results Links List Batch Open

To quickly understand a field, press Alt+1 ...2,3,4...Alt+5 on the search page of Google or Bing to open the search results of the first 2 nth power items and copy the opened ones link. Currently supports: Google, Bing, Zhihu.

目前為 2022-12-27 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name            [SNOLAB] Alt + 123... Searching Results Links List Batch Open
// @name:zh         [雪星实验室] Alt + 123... 一键批量打开谷歌必应搜索的前2的n次方项搜索结果
// @namespace       [email protected]
// @version         1.0.4
// @description     To quickly understand a field, press Alt+1 ...2,3,4...Alt+5 on the search page of Google or Bing to open the search results of the first 2 nth power items and copy the opened ones link. Currently supports: Google, Bing, Zhihu.
// @description:zh  快速了解一个领域用,在谷歌或必应的搜索页面 按 Alt+1 ...2,3,4... Alt+5  将会打开前2的n次方项的搜索结果,并复制打开的链接。目前支持:谷歌、必应、知乎。
// @author          snomiao
// @match           *://google.com/*
// @match           *://bing.com/*
// @match           *://youtube.com/*
// @match           *://zhihu.com/*
// @match           *://and-all-searching-results.com/*
// @match           *://*/*
// @grant           none
// ==/UserScript==

/*
TODO 修复列表识别算法于Google学术搜索的错误
错误例子
	[中国能源统计年鉴 - Google 学术搜索]( https://scholar.google.com/scholar?q=%E4%B8%AD%E5%9B%BD%E8%83%BD%E6%BA%90%E7%BB%9F%E8%AE%A1%E5%B9%B4%E9%89%B4&hl=zh-CN&as_sdt=0&as_vis=1&oi=scholart )
*/

globalThis.LinksListBatchUnload?.();
globalThis.LinksListBatchUnload = hotkeyMapper({
    "alt+1": () => tryOpenLinkByCount(2 ** 1),
    "alt+2": () => tryOpenLinkByCount(2 ** 2),
    "alt+3": () => tryOpenLinkByCount(2 ** 3),
    "alt+4": () => tryOpenLinkByCount(2 ** 4),
    "alt+5": () => tryOpenLinkByCount(2 ** 5),
    "alt+6": () => tryOpenLinkByCount(2 ** 6),
    "alt+7": () => tryOpenLinkByCount(2 ** 7),
    "alt+8": () => tryOpenLinkByCount(2 ** 8),
    "alt+9": () => tryOpenLinkByCount(2 ** 9),
    "shift+alt+1": () => tryCopyLinkByCount(2 ** 1),
    "shift+alt+2": () => tryCopyLinkByCount(2 ** 2),
    "shift+alt+3": () => tryCopyLinkByCount(2 ** 3),
    "shift+alt+4": () => tryCopyLinkByCount(2 ** 4),
    "shift+alt+5": () => tryCopyLinkByCount(2 ** 5),
    "shift+alt+6": () => tryCopyLinkByCount(2 ** 6),
    "shift+alt+7": () => tryCopyLinkByCount(2 ** 7),
    "shift+alt+8": () => tryCopyLinkByCount(2 ** 8),
    "shift+alt+9": () => tryCopyLinkByCount(2 ** 9),
});

// console.log('[谷歌一键打开前N项搜索结果] LOADED');
// 链接列获取();

function tryOpenLinkByCount(count = 1) {
    const links = getLinks(count);
    copyLinks(links);
    //在 chrome 下需要反过来才能让第1个链接先打开。。;
    links
        .map(({ 链接 }) => 链接)
        .reverse()
        .map(链接打开);
}

function tryCopyLinkByCount(count = 1) {
    const links = getLinks(count);
    copyLinks(links);
    return links;
}

function copyLinks(links) {
    textCopy(links.map(md格式链接生成).join("\n\n"));
}

function 最长共列阵输出(矩阵, a, b, x, y) {
    return !x || !y
        ? ""
        : a[x - 1] === b[y - 1]
        ? 最长共列阵输出(矩阵, a, b, x - 1, y - 1) + a[x - 1]
        : 矩阵[y][x - 1] > 矩阵[y - 1][x]
        ? 最长共列阵输出(矩阵, a, b, x - 1, y)
        : 最长共列阵输出(矩阵, a, b, x, y - 1);
}
function 最长共列(a, b) {
    const w = a.length,
        h = b.length;
    const m = Array(1 + h)
        .fill(0)
        .map(() => Array(1 + w).fill(0));
    for (let y = 0; y < h; y++)
        for (let x = 0; x < w; x++)
            m[1 + y][1 + x] =
                a[x] === b[y]
                    ? m[y][x] + 1
                    : Math.max(m[y][1 + x], m[1 + y][x]);
    false && console.table(m);
    return 最长共列阵输出(m, a, b, w, h);
}
function 元素特征列取(e) {
    return [...e.querySelectorAll("*")].map((e) => e?.tagName + e?.className);
}
function 特征元素筛函数(e) {
    return !"style script span".toUpperCase().split(" ").includes(e.tagName);
}
function 元素列表强度(e) {
    return (
        e.textContent.length *
        (e.children.length - 1) *
        [...e.children]
            .filter(特征元素筛函数)
            .map(元素特征列取)
            .map((_, i, a) => 最长共列(a[i], a[i + 1] || []))
            .reduce((前, 后) => 前 + 后.length, 0)
    );
}
function 列元素列提取() {
    return [...document.querySelectorAll("div,dl,ul,ol,tbody,table,td")]
        .filter((e) => e.children.length > 1)
        .map((元素) => ({
            元素,
            强度: 元素列表强度(元素),
            特征列: [...元素.children]
                .filter(特征元素筛函数)
                // .filter((e) => !["style", "script", "span"].includes(e.tagName))
                .map(元素特征列取),
        }))
        .sort((a, b) => -(a.强度 - b.强度));
}
function 标链元素提取(e) {
    return (
        e?.querySelector("dd,dt,h1,h2,h3,h4,h5,h6")?.querySelector("a") ||
        [...e?.querySelectorAll("a")]?.filter((e) =>
            e.querySelector("dd,dt,h1,h2,h3,h4,h5,h6")
        )?.[0] ||
        e?.querySelector("a")
    );
}
function 标链提取(元素) {
    return {
        元素,
        标题: 元素?.textContent?.replace(/\s+/g, " ").trim(),
        链接: 元素?.href,
    };
}
function 子列父亲节点筛函数(eFa, i, a) {
    return !a.some((eSon, j) => i != j && eFa.元素.contains(eSon.元素));
}
function 页主标链列提取() {
    return 列元素列提取()
        .filter(子列父亲节点筛函数)
        .filter((e, _, a) => e.强度 > a[0].强度 * 0.1) // 按最强者 10% 筛选
        .sort((a, b) => a.元素.offsetTop - b.元素.offsetTop) // 按垂直位置对比
        .filter(({ 元素, 强度, 特征列 }) => !console.log(元素, 强度, 特征列)) // 元素提取debug
        .map(({ 元素 }) => 元素) // 元素提取
        .flatMap((e) => [...e?.children]?.map?.(标链元素提取) || [])
        .filter((e) => e)
        .map(标链提取)
        .filter(({ 标题, 链接 }) => 标题)
        .filter(({ 标题, 链接 }) => 链接?.match?.(/^http/));
}
function textCopy(内容) {
    const input = document.createElement("textarea");
    input.setAttribute("readonly", "readonly");
    input.setAttribute("value", 内容);
    input.innerHTML = 内容;
    input.setAttribute("style", "position: fixed; top:0; left:0;z-index: 9999");
    document.body.appendChild(input);
    input.select();
    input.setSelectionRange(0, input.value.length);
    if (document.execCommand("copy")) {
        document.execCommand("copy");
        console.log(`长度为${内容.length}的内容已复制`);
    } else {
        // alert(`长度为${内容.length}的内容复制失败,请检查浏览器配置,或在页面上先按一下键盘任意键解除剪贴板保护`);
    }
    document.body.removeChild(input);
}
const 已打开过的链接 = {};
function 链接打开(链接) {
    if (!已打开过的链接[链接]) {
        window.open(链接);
    }
    已打开过的链接[链接] = 1;
}
function getLinks(数量 = Infinity) {
    return 页主标链列提取().slice(0, 数量);
}
function md格式链接生成({ 标题, 链接 }) {
    return `- [${标题}](${链接})`;
}
function hotkeyMapper(mapping) {
    const handlers = mapObjIndexed(hotkeyHandler, mapping);
    mapObjIndexed(
        (handler) => window.addEventListener("keydown", handler),
        handlers
    );
    return function unload() {
        return mapObjIndexed(
            (handler) => window.removeEventListener("keydown", handler),
            handlers
        );
    };
    function hotkeyHandler(fn, hotkey) {
        return (e) => {
            e[`${e.key}Key`] = true;
            const mods = "meta+alt+shift+ctrl";
            const conds = `${mods}+${hotkey}`
                .replace(/win|command|search/, "meta")
                .replace(/control/, "ctrl")
                .split("+")
                .map((k, i) => [k, Boolean(i >= 4) === Boolean(e[`${k}Key`])]);
            const covered = Object.entries(Object.fromEntries(conds));
            const matched = covered.every(([keyName, pass]) => pass);
            if (!matched) return;
            e.stopPropagation();
            e.preventDefault();
            return fn(e);
        };
    }
}

function mapObjIndexed(fn, obj) {
    if (arguments.length === 1) {
        return (_obj) => mapObject(fn, _obj);
    }
    let index = 0;
    const objKeys = keys(obj);
    const len = objKeys.length;
    const willReturn = {};
    while (index < len) {
        const key = objKeys[index];
        willReturn[key] = fn(obj[key], key, obj);
        index++;
    }
    return willReturn;
}