DuckDuckGo RTL Enhancer

Automatically applies RTL direction to DuckDuckGo results with Persian or Arabic text

  1. // ==UserScript==
  2. // @name DuckDuckGo RTL Enhancer
  3. // @name:fa داک‌داک‌گو با پشتیبانی راست به چپ
  4. // @name:ar داک داک جو - دعم الكتابة من اليمين إلى اليسار
  5. // @version 1.0
  6. // @description Automatically applies RTL direction to DuckDuckGo results with Persian or Arabic text
  7. // @description:fa به طور خودکار جهت راست به چپ را برای نتایج جستجوی داک‌داک‌گو با متن فارسی اعمال می‌کند
  8. // @description:ar يطبق تلقائيًا اتجاه من اليمين إلى اليسار على نتائج بحث داك داك جو مع النص العربي
  9. // @author Zen
  10. // @match https://duckduckgo.com/*
  11. // @run-at document-start
  12. // @license MIT
  13. // @supportURL https://github.com/Zen-CloudLabs/UserScripts/issues
  14. // @homepageURL https://github.com/Zen-CloudLabs/UserScripts
  15. // @namespace https://gf.qytechs.cn/users/1425911
  16. // ==/UserScript==
  17.  
  18. (function() {
  19. 'use strict';
  20. if (localStorage.getItem("rtlAutoEnabled") === "false") {
  21. return;
  22. }
  23.  
  24. const style = document.createElement('style');
  25. style.innerHTML = '@import url("https://fonts.googleapis.com/css2?family=Vazirmatn&display=swap");';
  26. document.head.appendChild(style);
  27.  
  28. const oneYearFromNow = new Date();
  29. oneYearFromNow.setFullYear(oneYearFromNow.getFullYear() + 1);
  30.  
  31. if (window.location.hostname === 'duckduckgo.com') {
  32. document.cookie = "t=Vazirmatn; domain=duckduckgo.com; path=/; expires=" +
  33. oneYearFromNow.toUTCString() + "; secure; SameSite=Lax;";
  34. }
  35.  
  36. const rtlCharRegex = /[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF]/g;
  37. const targetSelectors = '.PBQZNIcKgp0FJ_yxBVaB, .IwPq3HoxJc8guGUBwTKv';
  38. const excludedClass = 'JRDRiEf5NPKWK43sArdC';
  39.  
  40. function processElement(element) {
  41. if (!element || element.dataset.rtlApplied || element.classList.contains(excludedClass)) {
  42. return;
  43. }
  44.  
  45. const text = element.textContent.trim();
  46. if (!text) return;
  47.  
  48. const cleanText = text.replace(/\s/g, '');
  49. const totalChars = cleanText.length;
  50. if (totalChars === 0) return;
  51.  
  52. const rtlChars = cleanText.match(rtlCharRegex);
  53. const rtlCharCount = rtlChars ? rtlChars.length : 0;
  54. const rtlRatio = rtlCharCount / totalChars;
  55.  
  56. if (rtlRatio >= 0.4) {
  57. element.style.direction = "rtl";
  58. element.dataset.rtlApplied = "true";
  59. }
  60. }
  61.  
  62. function processAllElements() {
  63. document.querySelectorAll(targetSelectors).forEach(processElement);
  64. }
  65.  
  66. function handleMutations(mutationsList) {
  67. for (const mutation of mutationsList) {
  68. if (mutation.type === 'childList') {
  69. mutation.addedNodes.forEach(node => {
  70. if (node.nodeType === Node.ELEMENT_NODE) {
  71. if (node.matches && node.matches(targetSelectors)) {
  72. processElement(node);
  73. }
  74. node.querySelectorAll(targetSelectors).forEach(processElement);
  75. }
  76. });
  77. }
  78. else if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
  79. const target = mutation.target;
  80. if (target.matches && target.matches(targetSelectors)) {
  81. processElement(target);
  82. }
  83. }
  84. }
  85. }
  86.  
  87. const observer = new MutationObserver(handleMutations);
  88. const observerConfig = {
  89. childList: true,
  90. subtree: true,
  91. attributes: true,
  92. attributeFilter: ["class"]
  93. };
  94.  
  95. function initialize() {
  96. processAllElements();
  97. observer.observe(document.body, observerConfig);
  98. }
  99.  
  100. if (document.readyState !== 'loading') {
  101. initialize();
  102. } else {
  103. document.addEventListener("DOMContentLoaded", initialize);
  104. }
  105.  
  106. setInterval(processAllElements, 500);
  107. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址