Greasy Fork

Draggy

拖拽链接以在新标签页中打开,拖拽文本以在新标签页中搜索。

目前为 2024-10-02 提交的版本。查看 最新版本

// ==UserScript==
// @name         Draggy
// @name:zh-CN   Draggy
// @namespace    http://tampermonkey.net/
// @version      0.1.1
// @description  Drag a link to open in a new tab; drag a piece of text to search in a new tab.
// @description:zh-CN 拖拽链接以在新标签页中打开,拖拽文本以在新标签页中搜索。
// @author       PRO-2684
// @match        *://*/*
// @run-at       document-start
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @license      gpl-3.0
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_addValueChangeListener
// @require      https://update.greasyfork.org/scripts/470224/1456932/Tampermonkey%20Config.js
// ==/UserScript==
(function () {
    "use strict";
    const { name, version } = GM.info.script;
    const configDesc = {
        $default: {
            autoClose: false,
        },
        debug: {
            name: "Debug mode",
            title: "Enables debug mode.",
            type: "bool",
            value: false,
        },
        searchEngine: {
            name: "Search engine",
            title: "Search engine used when dragging text. Use {} as a placeholder for the URL-encoded query.",
            type: "string",
            value: "https://www.google.com/search?q={}",
        },
        maxLength: {
            name: "Maximum text length",
            title: "Maximum length of the search term. If the length of the search term exceeds this value, it will be truncated. Set to 0 to disable this feature.",
            type: "int_range-0-1000",
            value: 100,
        },
        maxTimeDelta: {
            name: "Maximum time delta",
            title: "Maximum time difference between esc/drop and dragend events to consider them as separate user gesture. Usually there's no need to change this value.",
            type: "int_range-1-100",
            value: 10,
        },
    };
    // TODO: ESC to cancel; drag distance threshold & circle overlay
    const config = new GM_config(configDesc, { immediate: false });
    /**
     * Last time a drop event occurred.
     * @type {number}
     */
    let lastDrop = 0;
    /**
     * Logs the given arguments if debug mode is enabled.
     * @param {...any} args The arguments to log.
     */
    function log(...args) {
        if (config.get("debug")) {
            console.log(`[${name}]`, ...args);
        }
    }
    /**
     * Searches for the given keyword.
     * @param {string} keyword The keyword to search for.
     */
    function search(keyword) {
        const searchEngine = config.get("searchEngine");
        const maxLength = config.get("maxLength");
        const truncated = maxLength > 0 ? keyword.slice(0, maxLength) : keyword;
        const url = searchEngine.replace("{}", encodeURIComponent(truncated));
        log(`Searching for "${truncated}" using "${url}"`);
        window.open(url, "_blank");
    }
    document.addEventListener("drop", (e) => {
        lastDrop = e.timeStamp;
        log("Drop event at", e.timeStamp);
    }, { passive: true });
    document.addEventListener("dragend", (e) => {
        if (e.timeStamp - lastDrop <= config.get("maxTimeDelta")) { // The gesture has been recognized as a drop and thus handled.
            log("Draggy interrupted by drop event");
            return;
        }
        if (e.dataTransfer.dropEffect !== "none" || e.dataTransfer.effectAllowed !== "uninitialized" || e.defaultPrevented) { // Only process drops that were not handled by the browser or other scripts.
            log("Draggy interrupted by other handler(s)");
            return;
        }
        log("Draggy starts processing...");
        e.preventDefault();
        const selectedText = window.getSelection()?.toString();
        if (selectedText) {
            search(selectedText);
            return;
        }
        const link = e.target.closest("a[href]");
        const href = link?.getAttribute("href");
        if (!href || href.startsWith("javascript:") || href === "#") {
            log("Draggy can't find selected text or a valid link");
            return;
        }
        window.open(link.href, "_blank");
    }, { passive: false });
    log(`${version} initialized successfully 🎉`);
})();