Greasy Fork

Greasy Fork is available in English.

左键限制解除

左键选中

当前为 2025-02-13 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name              左键限制解除
// @description       左键选中
// @version           1.1
// @match             *://*/*
// @run-at            document-start
// @grant             none
// @namespace http://greasyfork.icu/users/12375
// ==/UserScript==

(function() {
  'use strict';

  // 默认规则配置
  var rule = {
    name: "default",
    hook_eventNames: "contextmenu|select|selectstart|copy|cut|dragstart",
    unhook_eventNames: "keydown|keyup",
    dom0: true,
    hook_addEventListener: true,
    hook_preventDefault: true,
    hook_set_returnValue: true
  };

  // 要处理的 event 列表
  var hook_eventNames, unhook_eventNames, eventNames;
  // 储存名称
  var storageName = getRandStr('qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM', parseInt(Math.random() * 12 + 8));
  // 储存被 Hook 的函数
  var EventTarget_addEventListener = EventTarget.prototype.addEventListener;
  var document_addEventListener = document.addEventListener;
  var Event_preventDefault = Event.prototype.preventDefault;

  // 清理循环
  function clearLoop() {
    var elements = getElements();

    for(var i in elements) {
      for(var j in eventNames) {
        var name = 'on' + eventNames[j];
        if(elements[i][name] !== null && elements[i][name] !== onxxx) {
          if(unhook_eventNames.indexOf(eventNames[j]) >= 0) {
            elements[i][storageName + name] = elements[i][name];
            elements[i][name] = onxxx;
          } else {
            elements[i][name] = null;
          }
        }
      }
    }
  }

  // 返回true的函数
  function returnTrue(e) {
    return true;
  }
  function unhook_t(e) {
    return unhook(e, this, storageName + e.type + 't');
  }
  function unhook_f(e) {
    return unhook(e, this, storageName + e.type + 'f');
  }
  function unhook(e, self, funcsName) {
    var list = self[funcsName];
    for(var i in list) {
      list[i](e);
    }
    e.returnValue = true;
    return true;
  }
  function onxxx(e) {
    var name = storageName + 'on' + e.type;
    this[name](e);
    e.returnValue = true;
    return true;
  }

  // 获取随机字符串
  function getRandStr(chs, len) {
    var str = '';

    while(len--) {
      str += chs[parseInt(Math.random() * chs.length)];
    }
    return str;
  }

  // 获取所有元素 包括document
  function getElements() {
    var elements = Array.prototype.slice.call(document.getElementsByTagName('*'));
    elements.push(document);
    return elements;
  }

  // 初始化
  function init() {

    // hook addEventListener
    if(rule.hook_addEventListener) {
      EventTarget.prototype.addEventListener = addEventListener;
      document.addEventListener = addEventListener;
    }

  }
  init();

  // 视频控制保护逻辑
  const EV = {
    select: 1, selectstart: 2, copy: 4, cut: 8, contextmenu: 16,
    mousedown: 32, mouseup: 64, mousemove: 128, click: 256
  };

  const videoElements = new WeakMap();
  const isVideoControl = (target) => {
    while(target) {
      if(videoElements.has(target)) return videoElements.get(target);
      if(target.tagName === 'VIDEO' ||
         target.classList.contains('progress-bar') ||
         target.closest('video, [role="video-controls"]')) {
        videoElements.set(target, true);
        return true;
      }
      target = target.parentElement;
    }
    return false;
  };

  const { addEventListener: protoAdd } = EventTarget.prototype;
  const { preventDefault: protoPrevent } = Event.prototype;

  EventTarget.prototype.addEventListener = function(type, listener, options) {
    const eventFlag = EV[type] || 0;
    if(eventFlag & 0b00011111 && !isVideoControl(this)) {
      protoAdd.call(this, type, e => e.stopImmediatePropagation(), { capture: true, ...options });
    } else {
      protoAdd.call(this, type, listener, options);
    }
  };

})();