Greasy Fork is available in English.

AI Everywhere

Mini A.I. floating menu that can define words, answer questions, translate, and much more in a single click and with your custom prompts. Includes useful click to search on Google and copy selected text buttons, along with Rocker+Mouse Gestures and Units+Currency+Time zone Converters, all features can be easily modified or disabled.

Nainštalovať tento skript?
Autor skriptu navrhuje

Tiež sa vám môže páčiť Precise Time Converter on Google.

Nainštalovať tento skript
// ==UserScript==
// @name               AI Everywhere
// @namespace          OperaBrowserGestures
// @description        Mini A.I. floating menu that can define words, answer questions, translate, and much more in a single click and with your custom prompts. Includes useful click to search on Google and copy selected text buttons, along with Rocker+Mouse Gestures and Units+Currency+Time zone Converters, all features can be easily modified or disabled.
// @version            73
// @author             hacker09
// @include            *
// @exclude            https://accounts.google.com/v3/signin/*
// @icon               https://i.imgur.com/8iw8GOm.png
// @grant              GM_registerMenuCommand
// @grant              GM_getResourceText
// @grant              GM.xmlHttpRequest
// @grant              GM_setClipboard
// @grant              GM_deleteValue
// @grant              GM_openInTab
// @grant              window.close
// @run-at             document-end
// @grant              GM_setValue
// @grant              GM_getValue
// @connect            google.com
// @connect            generativelanguage.googleapis.com
// @resource           AICSS https://hacker09.glitch.me/AICSS.css
// @resource           AIMenuHTMLContent https://hacker09.glitch.me/AIMenuHTML.html
// @resource           AIScript https://greasyfork.org/en/scripts/419825-ai-everywhere.json
// @require            https://update.greasyfork.org/scripts/506699/1440902/marked.js
// ==/UserScript==

/* jshint esversion: 11 */

const BypassTT = window.trustedTypes?.createPolicy('BypassTT', { createHTML: HTML => HTML }); //Bypass trustedTypes

if (GM_info.script.version !== JSON.parse(GM_getResourceText('AIScript')).version) { //Open the update page if a newer version is available
  GM_openInTab(GM_info.scriptUpdateURL, { active: true, setParent: true });
}

if (GM_getResourceText('AIMenuHTMLContent') === '' || GM_getResourceText('AICSS') === '') {
  alert('Failed to load the CSS and HTML resource files.\n\nPlease contact your network administrator to unblock the https://glitch.me domain.');
  return; //Stop execution
}

if ((location.href === 'https://aistudio.google.com/app/apikey' && document.querySelector(".apikey-link") !== null) && GM_getValue("APIKey") === undefined || GM_getValue("APIKey") === null || GM_getValue("APIKey") === '') { //Set up the API Key
  window.onload = setTimeout(function() {
    document.querySelectorAll(".apikey-link")[1].click(); //Click on the API Key
    setTimeout(function() {
      GM_setValue("APIKey", document.querySelector(".apikey-text").innerText); //Store the API Key
      (GM_getValue("APIKey") !== undefined && GM_getValue("APIKey") !== null && GM_getValue("APIKey") !== '') ? alert('API Key automatically added!') : alert('Failed to automatically add API Key!');
    }, 500);
  }, 1000);
}

//Mouse Gestures__________________________________________________________________________________________________________________________________________________________________________________
GM_registerMenuCommand("Enable/Disable Mouse Gestures", MouseGestures);

function MouseGestures() //Enable/disable MouseGestures
{
  if (GM_getValue("MouseGestures") === true) {
    GM_setValue("MouseGestures", false);
  }
  else {
    GM_setValue("MouseGestures", true);
    location.reload();
  }
}

if (GM_getValue("MouseGestures") === true) //If the MouseGestures is enabled
{
  const SENSITIVITY = 3;
  const TOLERANCE = 3;
  const funcs = { //Store the MouseGestures functions

    'L': function() { //Detect the Left movement
      window.history.back();
    },

    'R': function() { //Detect the Right movement
      window.history.forward();
    },

    'D': function() { //Detect the Down movement
      if (IsShiftNotPressed === true) {
        GM_openInTab(link, { active: true, insert: true, setParent: true });
      }
    },

    'UD': function() { //Detect the Up+Down movement
      location.reload();
    },

    'DR': function(e) { //Detect the Down+Right movement
      top.close();
      e.preventDefault();
      e.stopPropagation();
    },

    'DU': function() { //Detect the Down+Up movement
      GM_openInTab(link, {
        active: false,
        insert: true,
        setParent: true
      });
    }

  };

  //Math codes to track the mouse movement gestures
  var x, y, path;
  const s = 1 << ((7 - SENSITIVITY) << 1);
  const t1 = Math.tan(0.15708 * TOLERANCE),t2 = 1 / t1;

  const tracer = function(e) {
    var cx = e.clientX, cy = e.clientY, deltaX = cx - x, deltaY = cy - y, distance = deltaX * deltaX + deltaY * deltaY;
    if (distance > s) {
      var slope = Math.abs(deltaY / deltaX), direction = '';
      if (slope > t1) {
        direction = deltaY > 0 ? 'D' : 'U';
      } else if (slope <= t2) {
        direction = deltaX > 0 ? 'R' : 'L';
      }
      if (path.charAt(path.length - 1) !== direction) {
        path += direction;
      }
      x = cx;
      y = cy;
    }
  };

  window.addEventListener('mousedown', function(e) {
    if (e.which === 3) {
      x = e.clientX;
      y = e.clientY;
      path = "";
      window.addEventListener('mousemove', tracer, false); //Detect the mouse position
    }
  }, false);

  var IsShiftNotPressed = true; //Store the shift key status
  window.addEventListener("contextmenu", function(e) { //When the shift key is/isn't pressed
    if (e.shiftKey) {
      IsShiftNotPressed = false;
      open(link, '_blank', 'height=' + screen.height + ',width=' + screen.width);
    }
    if (LeftClicked === true) { //If the Left Click was released when the Rocker Mouse Gestures were enabled
      e.preventDefault();
      e.stopPropagation();
    }
    setTimeout(function() {
      IsShiftNotPressed = true;
    }, 500);
  }, false);

  window.addEventListener('contextmenu', function(e) { //When the right click BTN is released
    window.removeEventListener('mousemove', tracer, false); //Track the mouse movements
    if (path !== "") {
      e.preventDefault();
      if (funcs.hasOwnProperty(path)) {
        funcs[path]();
      }
    }
  }, false);

  var link;
  Array.from(document.querySelectorAll('a')).forEach(Element => Element.onmouseover = function() {
    link = this.href; //Store the hovered link to a variable
  });

  Array.from(document.querySelectorAll('a')).forEach(Element => Element.onmouseout = function() {
    const PreviousLink = link; //Save the hovered link
    setTimeout(function() {
      if (PreviousLink === link) //If the hovered link is still the same as the previously hovered Link
      {
        link = 'about:newtab'; //Make the script open a new browser tab when the mouse leaves any link that was hovered
      }
    }, 200);
  });
}

//Rocker Mouse Gestures___________________________________________________________________________________________________________________________________________________________________________
GM_registerMenuCommand("Enable/Disable Rocker Mouse Gestures", RockerMouseGestures);

function RockerMouseGestures() //Enable/disable RockerMouseGestures
{
  if (GM_getValue("RockerMouseGestures") === true) {
    GM_setValue("RockerMouseGestures", false);
  }
  else {
    GM_setValue("RockerMouseGestures", true);
    location.reload();
  }
}

if (GM_getValue("RockerMouseGestures") === true || GM_getValue("SearchHiLight") === true) //If the RockerMouseGestures or the SearchHiLight is enabled
{
  var LeftClicked, RightClicked;
  window.addEventListener("mousedown", function(e) { //Track which side of the mouse was the first one to be pressed
    switch (e.button) {
      case 0:
        LeftClicked = true;
        break;
      case 2:
        RightClicked = true;
        break;
    }
  }, false);

  window.addEventListener("mouseup", function(e) { //Track which side of the mouse was the last one to be released
    switch (e.button) {
      case 0:
        LeftClicked = false;
        break;
      case 2:
        RightClicked = false;
        break;
    }
    if (LeftClicked && RightClicked === false) { //If Left was Clicked and then Right Click was released
      history.back();
    }
    if (RightClicked && LeftClicked === false) { //If Right was Clicked and then Left Click was released
      history.forward();
    }
  }, false);
}

//SearchHighLight + CurrenciesConverter + UnitsConverter__________________________________________________________________________________________________________________________________________
GM_registerMenuCommand("Enable/Disable SearchHiLight", SearchHiLight);
if (GM_getValue("SearchHiLight") === undefined) { //Set up everything on the first run
  GM_setValue("SearchHiLight", true);
  GM_setValue("MouseGestures", true);
  GM_setValue("TimeConverter", true);
  GM_setValue("UnitsConverter", true);
  GM_setValue("RockerMouseGestures", false);
  GM_setValue("CurrenciesConverter", true);
}

function SearchHiLight() //Enable/disable the SearchHiLight and the Currency/Unit converters
{
  if (GM_getValue("SearchHiLight") === true) {
    GM_setValue("SearchHiLight", false);
    GM_setValue("TimeConverter", false);
    GM_deleteValue('YourLocalCurrency');
    GM_setValue("UnitsConverter", false);
    GM_setValue("CurrenciesConverter", false);
  }
  else {
    GM_setValue("SearchHiLight", true);

    if (confirm('If you want to enable the Currency Converter press OK.'))
    {
      GM_setValue("CurrenciesConverter", true);
    }
    else
    {
      GM_setValue("CurrenciesConverter", false);
    }

    if (confirm('If you want to enable the Units Converter press OK.'))
    {
      GM_setValue("UnitsConverter", true);
    }
    else
    {
      GM_setValue("UnitsConverter", false);
    }
    location.reload();
  }
}

if (GM_getValue("SearchHiLight") === true) //If the SearchHiLight is enabled
{
  var SelectedTextIsLink, FinalCurrency, SelectedText, SelectedTextSearch = '';
  const Links = new RegExp(/\.org|\.ly|\.net|\.co|\.tv|\.me|\.biz|\.club|\.site|\.br|\.gov|\.io|\.jp|\.edu|\.au|\.in|\.it|\.ca|\.mx|\.fr|\.tw|\.il|\.uk|\.zoom\.us|\youtu.be/i);

  document.body.addEventListener('mouseup', function() { //When the user releases the mouse click after selecting something
    HtmlMenu.style.display = 'block'; //Display the container div
    SelectedText = getSelection().toString(); //Store the selected text
    SelectedTextSearch = getSelection().toString().replaceAll('&', '%26'); //Store the selected text to be opened on Google
    shadowRoot.querySelector("#ShowCurrencyORUnits").innerText = ''; //Remove the previous Units/Currency text

    function ShowConvertion(UnitORCurrency, Type, Result) {
      shadowRoot.querySelector("#SearchBTN span")?.remove(); //Return previous HTML
      shadowRoot.querySelector("#SearchBTN").innerHTML = (html => BypassTT?.createHTML(html) || html)('<span class="GreyBar">│ </span>' + shadowRoot.querySelector("#SearchBTN").innerHTML);

      if (UnitORCurrency === 'Currencies') {
        const hasSymbol = SelectedText.match(Currencies)[2].match(CurrencySymbols) !== null;
        const currencyFormat = Intl.NumberFormat(navigator.language, {
          style: 'currency',
          currency: GM_getValue("YourLocalCurrency")
        }).format(Result);

        const displayText = hasSymbol ? (Type + ' 🠂 ' + currencyFormat) : currencyFormat;
        shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(displayText);
      }
      else
      {
        UnitORCurrency === 'Units' ? shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(`${Result} ${Type}`) : shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(Result); //Show the converted time results
      }

      setTimeout(() => { //Wait for Units to show up to get the right offsetWidth
        const offsetWidth = shadowRoot.querySelector("#ShowCurrencyORUnits").offsetWidth; //Store the current menu size
        shadowRoot.querySelector("#ShowCurrencyORUnits").onmouseover = function() { //When the mouse hovers the unit/currency
          shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(`Copy`);
          shadowRoot.querySelector("#ShowCurrencyORUnits").style.display = 'inline-flex';
          shadowRoot.querySelector("#ShowCurrencyORUnits").style.width = `${offsetWidth}px`; //Maintain the aspect ratio
        };
      }, 0);

      const htmlcode = shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML; //Save the converted unit/currency value
      shadowRoot.querySelector("#ShowCurrencyORUnits").onmouseout = function() { //When the mouse leaves the unit/currency
        shadowRoot.querySelector("#ShowCurrencyORUnits").style.width = ''; //Return the original aspect ratio
        shadowRoot.querySelector("#ShowCurrencyORUnits").style.display = ''; //Return the original aspect ratio
        shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(htmlcode); //Return the previous html
      };

      shadowRoot.querySelector("#ShowCurrencyORUnits").onclick = function() { //When the unit/currency is clicked
        UnitORCurrency.match(/Units|Time/) ? GM_setClipboard(`${Result} ${Type}`) : GM_setClipboard(Intl.NumberFormat(navigator.language, { style: 'currency', currency: GM_getValue("YourLocalCurrency") }).format(Result));
      };
    }

    //Time Converter______________________________________________________________________________________________________________________________________________________________________________
    GM_registerMenuCommand("Enable/Disable Time Converter", TimeConverter);

    function TimeConverter() //Enable/disable TimeConverter
    {
      if (GM_getValue("TimeConverter") === true) {
        GM_setValue("TimeConverter", false);
      }
      else {
        GM_setValue("TimeConverter", true);
        location.reload();
      }
    }

    const time = new RegExp(/^[ \t\xA0]*(?=.*?(\d{1,2}:\d{2}(?::\d{2})?\s?(?:[aApP]\.?[mM]\.?)?)|\d{1,2}(?::\d{2}(?::\d{2})?)?\s?(?:[aApP]\.?[mM]\.?)?)(?=.*?(PST|PDT|MST|MDT|CST|CDT|EST|EDT|AST|ADT|NST|NDT|GMT|BST|MET|CET|CEST|EET|EEST|WET|WEST|JST|KST|IST|MSK))(?:\1[ \t\xA0]*\2|\2[ \t\xA0]*\1)[ \t\xA0]*$/i);

    if (GM_getValue("TimeConverter") === true && SelectedText.match(time) !== null) //If the TimeConverter is enabled and if the selected text is a time
    {
      GM.xmlHttpRequest({ //Get the final converted time value
        method: "GET",
        url: `https://www.google.com/search?q=${SelectedText.match(time)[0].replace("WET","Western European Time")} to local time`,
        onload: (response) => {
          const newDocument = new DOMParser().parseFromString(response.responseText, 'text/html'); //Parse the fetch response
          const ConvertedTime = newDocument.querySelector(".aCAqKc")?.innerText; //Store the Converted time
          const Secs = SelectedText.match(time)[0].match(/(?:\d{1,2}:\d{2}(:\d{2})\s?[ap]\.?m)/i) !== null ? SelectedText.match(time)[0].match(/(?:\d{1,2}:\d{2}(:\d{2})\s?[ap]\.?m)/i)[1] : '';
          ConvertedTime !== undefined ? ShowConvertion('Time', '', ConvertedTime.replace(/(\d{1,2}):(\d{2})\s?([pP][mM])/, (_, h, m) => `${(h % 12 + 12) % 24}:${m}`).match(/[\d:]+/g)[0] + Secs) : ''; //Convert PM to 24-hour format if we got the final time convertion value
        }
      });
    }
    //CurrenciesConverter____________________________________________________________________________________________________________________________________________________________________________
    const CurrencySymbols = new RegExp(/\$|R\$|HK\$|US\$|\$US|¥|€|Rp|Kč|kr(?!w)|zł|£|฿|₩|лв|₪|円|₱|₽|руб|lei|Fr|krw|RON|TRY|₿|Br|₾|₴|₸|₺/i);
    const Currencies = new RegExp(/^[ \t\xA0]*\$?(?=.*?(\d+(?:.*\d+)?))(?=(?:\1[ \t\xA0]*)?(Dólares|dolares|dólares|dollars|AUD|BGN|BRL|BCH|BTC|BYN|CAD|CHF|Fr|CNY|CZK|DKK|EUR|EGP|ETH|GBP|GEL|HKD|HUF|IDR|ILS|INR|JPY|LTC|KRW|MXN|NOK|NZD|PHP|PLN|RON|RUB|SEK|SGD|THB|TRY|USD|UAH|ZAR|KZT|YTL|\$|R\$|HK\$|US\$|\$US|¥|€|Rp|Kč|kr|krw|zł|£|฿|₩|лв|₪|円|₱|₽|руб|lei|Kč|₿|Br|₾|₴|₸|₺))(?:\1[ \t\xA0]*\2|\2[ \t\xA0]*\1)[ \t\xA0]*$/i);

    if (GM_getValue("CurrenciesConverter") === true && SelectedText.match(Currencies) !== null) { //If Currencies Converter is enabled and if the selected text is a currency
      if (GM_getValue("YourLocalCurrency") === undefined) {
        const UserInput = prompt('Write your local currency.\nThe script will always use your local currency to make exchange-rate conversions.\n\n*Currency input examples:\nBRL\nCAD\nUSD\netc...\n\n*Press OK');
        GM_setValue("YourLocalCurrency", UserInput);
      }
      const currencyMap = { '$': 'USD', 'us$': 'USD', '$us': 'USD', 'r$': 'BRL', 'hk$': 'HKD', '¥': 'JPY', '€': 'EUR', 'rp': 'IDR', 'kč': 'CZK', 'kr': 'NOK', 'zł': 'PLN', '£': 'GBP', '฿': 'THB', '₩': 'KRW', 'лв': 'BGN', '₪': 'ILS', '円': 'JPY', '₱': 'PHP', '₽': 'RUB', 'руб': 'RUB', 'lei': 'RON', 'ron': 'Romanian Leu', 'krw': 'KRW', 'fr': 'CHF', '₿': 'BTC', 'Br': 'BYN', '₾': 'GEL', '₴': 'UAH', '₸': 'KZT', '₺': 'YTL', 'try': 'Turkish Lira' };
      const CurrencySymbol = currencyMap[SelectedText.match(CurrencySymbols)?.[0].toLowerCase()] || SelectedText.match(Currencies)[2]; //Store the currency symbol

      GM.xmlHttpRequest({ //Get the final converted currency value
        method: "GET",
        url: `https://www.google.com/search?q=${SelectedText.match(Currencies)[1].replaceAll(',','')} ${CurrencySymbol} in ${GM_getValue("YourLocalCurrency")}`,
        onload: (response) => {
          const newDocument = new DOMParser().parseFromString(response.responseText, 'text/html'); //Parse the fetch response
          const FinalCurrency = parseFloat(newDocument.querySelector(".SwHCTb, .pclqee").innerText.split(' ')[0].replaceAll(',', '')); //Store the FinalCurrency and erase all commas
          ShowConvertion('Currencies', CurrencySymbol, FinalCurrency);
        }
      });
    }
    //UnitsConverter_________________________________________________________________________________________________________________________________________________________________________________
    const conversionMap = {};
    const Units = new RegExp(/^[ \t\xA0]*(-?\d+(?:[., ]\d+)?)(?:[ \t\xA0]*x[ \t\xA0]*(-?\d+(?:[., ]\d+)?))?[ \t\xA0]*(in|inch|inches|"|”|″|cm|cms|centimeters?|m|mt|mts|meters?|ft|kg|lbs?|pounds?|kilograms?|ounces?|g|ozs?|fl oz|fl oz (us)|fluid ounces?|kphs?|km\/h|kilometers per hours?|mhp|mphs?|meters per hours?|(?:°\s?|º\s?|)(?:degrees?\s+)?(?:celsius|fahrenheit|[CF])|km\/hs?|ml|milliliters?|l|liters?|litres?|gal|gallons?|yards?|yd|Millimeter|millimetre|kilometers?|mi|mm|miles?|km|ft|fl|feets?|grams?|kilowatts?|kws?|brake horsepower|mechanical horsepower|hps?|bhps?|miles per gallons?|mpgs?|liters per 100 kilometers?|lt?\/100km|liquid quarts?|lqs?|qt|foot-? ?pounds?|ft-?lbs?|lb fts?|newton-? ?meters?|n·?m|\^\d+)[ \t\xA0]*(?:\(\w+\)[ \t\xA0]*)?$/i);

    function addConversion(keys, unit, factor, convert) { //Helper function to add multiple keys with the same value
      keys.forEach(key => { conversionMap[key] = { unit, factor, convert }; });
    }

    if (GM_getValue("UnitsConverter") === true && SelectedText.match(Units) !== null) { //If the Units Converter option is enabled and if the selected text is an unit
      addConversion(['inch', 'inches', 'in', '"', '”', '″'], 'cm', 2.54);
      addConversion(['centimeter', 'centimeters', 'cm', 'cms'], 'in', 1/2.54);
      addConversion(['meter', 'meters', 'm', 'mt', 'mts'], 'ft', 3.281);
      addConversion(['kilogram', 'kilograms', 'kg'], 'lb', 2.205);
      addConversion(['pound', 'pounds', 'lb', 'lbs'], 'kg', 1/2.205);
      addConversion(['ounce', 'ounces', 'oz', 'ozs'], 'g', 28.35);
      addConversion(['gram', 'grams', 'g'], 'oz', 1/28.35);
      addConversion(['kilometer', 'kilometers', 'km'], 'mi', 1/1.609);
      addConversion(['kph', 'kphs', 'km/h', 'km/hs', 'kilometers per hour', 'kilometers per hours'], 'mph', 0.621371);
      addConversion(['mph', 'mphs', 'meters per hour', 'meters per hours'], 'km/h', 1.609);
      addConversion(['mi', 'mile', 'miles'], 'km', 1.609);
      addConversion(['°c', 'ºc', 'celsius', 'degrees celsius', '° celsius', 'º celsius'], '°F', null, v => (v*9/5)+32);
      addConversion(['°f', 'ºf', 'fahrenheit', 'degrees fahrenheit', '° fahrenheit', 'º fahrenheit'], '°C', null, v => (v-32)*5/9);
      addConversion(['milliliter', 'milliliters', 'ml'], 'fl oz (US)', 1/29.574);
      addConversion(['fl oz (US)', 'fl oz', 'fl', 'fluid ounce', 'fluid ounces'], 'ml', 29.574);
      addConversion(['litre', 'liter', 'litres', 'liters', 'l'], 'gal (US)', 1/3.785);
      addConversion(['gal', 'gallon', 'gallons'], 'l', 3.785);
      addConversion(['yard', 'yards', 'yd'], 'm', 1/1.094);
      addConversion(['millimetre', 'millimeters', 'millimetres', 'mm'], 'in', 1/25.4);
      addConversion(['feet', 'feets', 'ft'], 'm', 0.3048);
      addConversion(['kilowatt', 'kilowatts', 'kw', 'kws'], 'hp', 1.341);
      addConversion(['mhp', 'mhps', 'hp', 'hps', 'brake horsepower', 'mechanical horsepower'], 'kW', 1/1.341);
      addConversion(['mpg', 'mpgs', 'miles per gallon', 'miles per gallons'], 'l/100km', null, v => 235.215/v);
      addConversion(['l/100km','lt/100km', 'liters per 100 kilometer', 'liters per 100 kilometers'], 'mpg (US)', null, v => 235.215/v);
      addConversion(['qt', 'lq', 'lqs', 'liquid quart', 'liquid quarts'], 'l', 1/1.057);
      addConversion(['foot-pound', 'foot-pounds', 'foot pound', 'foot pounds', 'ft-lbs', 'ft-lb', 'ft lbs', 'ft lb', 'lb ft', 'lb-ft'], 'Nm', 1.3558179483);
      addConversion(['nm','n·m', 'newton-meter', 'newton-meters', 'newton meter', 'newton meters'], 'lbf ft', 1/1.3558179483);

      const selectedUnitType = SelectedText.match(Units)[3].toLowerCase();
      const SelectedUnitValue = SelectedText.match(Units)[1].replaceAll(',', '.');
      const SecondSelectedUnitValue = SelectedText.match(Units)[2]?.replaceAll(',', '.') || 0;

      const convertValue = (value, unitType) => {
        const { factor, convert } = conversionMap[unitType] || {};
        return convert ? convert(value) : value * factor;
      };

      var NewUnit = conversionMap[selectedUnitType]?.unit || selectedUnitType;
      var ConvertedUnit = SecondSelectedUnitValue != 0 ? `${convertValue(parseFloat(SelectedUnitValue), selectedUnitType).toFixed(2)} x ${convertValue(parseFloat(SecondSelectedUnitValue), selectedUnitType).toFixed(2)}` : convertValue(parseFloat(SelectedUnitValue), selectedUnitType).toFixed(2);
      ConvertedUnit = SelectedText.match(/\^(\d+\.?\d*)/) ? (NewUnit = 'power', Math.pow(parseFloat(SelectedUnitValue), parseFloat(SelectedText.match(/\^(\d+\.?\d*)/)[1]))) : ConvertedUnit;
      ShowConvertion('Units', NewUnit, ConvertedUnit);
    }
    //Menu___________________________________________________________________________________________________________________________________________________________________________________________
    if (shadowRoot.querySelector("#SearchBTN").innerText === 'Open') //If the Search BTN text is 'Open'
    {
      shadowRoot.querySelector("#highlight_menu > ul").style.paddingInlineStart = '19px'; //Increase the menu size
      shadowRoot.querySelector("#SearchBTN").innerText = 'Search'; //Display the BTN text as Search again
      shadowRoot.querySelectorAll(".AI-BG-box button").forEach(button => { button.style.marginLeft = ''; }); //Remove the margin left
      shadowRoot.querySelector("#OpenAfter").remove(); //Remove the custom Open white hover overlay
      SelectedTextIsLink = false; //Make common words searchable again
    }

    if (SelectedText.match(Links) !== null) //If the selected text is a link
    {
      SelectedTextIsLink = true;
      shadowRoot.querySelector("#highlight_menu > ul").style.paddingInlineStart = '27px'; //Increase the menu size
      shadowRoot.querySelector("#SearchBTN").innerText = 'Open'; //Change the BTN text to Open
      shadowRoot.querySelectorAll(".AI-BG-box button").forEach(button => { button.style.marginLeft = '-2%'; }); //Add a margin left
      shadowRoot.innerHTML += (html => BypassTT?.createHTML(html) || html)(` <style id="OpenAfter"> #SearchBTN::after { width: 177% !important; transform: translate(-34%, -71%) !important; } </style> `); //Add a custom Open white hover overlay
    }

    shadowRoot.querySelector("#SearchBTN").onmousedown = function() {
      var LinkfyOrSearch = 'https://www.google.com/search?q=';
      if (SelectedTextIsLink === true)
      {
        LinkfyOrSearch = 'https://'; //Make the non-HTTP and non-HTTPS links able to be opened
      }
      if (SelectedText.match(/http:|https:/) !== null) //If the selected text is a link that already has HTTP or HTTPS
      {
        LinkfyOrSearch = ''; //Remove the https:// that was previously added to this var
      }

      GM_openInTab(LinkfyOrSearch + SelectedTextSearch, { active: true, setParent: true, loadInBackground: true }); //Open google and search for the selected text
      getSelection().removeAllRanges(); //UnSelect the selected text after the search BTN is clicked so that if the user clicks on the past selected text the menu won't show up again.
      shadowRoot.querySelector("#highlight_menu").classList.remove('show'); //Hide the menu
    };

    const menu = shadowRoot.querySelector("#highlight_menu");
    if (document.getSelection().toString().trim() !== '') { //If text has been selected
      const p = document.getSelection().getRangeAt(0).getBoundingClientRect(); //Store the selected position

      menu.classList.add('show'); //Show the menu
      menu.offsetHeight; //Trigger reflow by forcing a style calculation
      menu.style.left = p.left + (p.width / 2) - (menu.offsetWidth / 2) + 'px';
      menu.style.top = p.top - menu.offsetHeight - 10 + 'px';
      menu.classList.add('highlight_menu_animate');

      return; //Keep the menu open
    }
    menu.classList.remove('show'); //Hide the menu
    shadowRoot.querySelector("#SearchBTN span")?.remove(); //Return previous HTML
  }); //Finishes the mouseup event listener

  //AI Menu_______________________________________________________________________________________________________________________________________________________________________________________
  const AICSS = GM_getResourceText("AICSS");
  var desiredVoice = null, isRecognizing = false;
  const HtmlMenu = document.createElement('div'); //Create a container div
  HtmlMenu.setAttribute('style', `width: 0px; height: 0px; display: none;`); //Hide the container div by default
  const shadowRoot = HtmlMenu.attachShadow({ mode: 'closed' });
  const BGColor = matchMedia('(prefers-color-scheme: dark)').matches ? 'rgb(37, 36, 53)' : '#e7edf1'; //Change AI theme according to the browser theme
  const IMGsColor = BGColor === '#e7edf1' ? 'filter: invert(1)' : ''; //If on white mode invert black svg colors to white
  const TextColor = BGColor === '#e7edf1' ? 'black' : 'white'; //Depending on the browser theme change the AI menu text color
  const UniqueLangs = navigator.languages.filter((l, i, arr) => !arr.slice(0, i).some(e => e.split('-')[0].toLowerCase() === l.split('-')[0].toLowerCase()) ); //Filter unique languages
  const Lang = UniqueLangs.length > 1 ? `${UniqueLangs[0]} and into ${UniqueLangs[1]}` : UniqueLangs[0]; //Use 1 or 2 languages
  const GeminiSVG = '<svg viewBox="0 0 32 32" fill="none"> <path d="M14 28C14 26.0633 13.6267 24.2433 12.88 22.54C12.1567 20.8367 11.165 19.355 9.905 18.095C8.645 16.835 7.16333 15.8433 5.46 15.12C3.75667 14.3733 1.93667 14 0 14C1.93667 14 3.75667 13.6383 5.46 12.915C7.16333 12.1683 8.645 11.165 9.905 9.905C11.165 8.645 12.1567 7.16333 12.88 5.46C13.6267 3.75667 14 1.93667 14 0C14 1.93667 14.3617 3.75667 15.085 5.46C15.8317 7.16333 16.835 8.645 18.095 9.905C19.355 11.165 20.8367 12.1683 22.54 12.915C24.2433 13.6383 26.0633 14 28 14C26.0633 14 24.2433 14.3733 22.54 15.12C20.8367 15.8433 19.355 16.835 18.095 18.095C16.835 19.355 15.8317 20.8367 15.085 22.54C14.3617 24.2433 14 26.0633 14 28Z" fill="url(#paint)"></path></svg>';

  shadowRoot.innerHTML = (html => BypassTT?.createHTML(html) || html)(GM_getResourceText("AIMenuHTMLContent").replaceAll(/\$\{(AICSS|BGColor|TextColor|GeminiSVG|IMGsColor)\}/g, (_, key) => ({AICSS, BGColor, TextColor, GeminiSVG, IMGsColor, }[key]))); //Set the AI menu html

  function Generate(Prompt, button) { //Call the AI endpoint
    const context = !!shadowRoot.querySelector("#context.show") ? `(You're not allowed to say anything like "Based on the provided text")\n"${Prompt} mainly base yourself on the text below\n${document.body.innerText}` : Prompt; //Add the page context if context is enabled
    const IsQuestion = Prompt.includes('?') ? 'Give me a very short, then a long detailed answer' : 'Help me further explore a term or topic from the text/word';
    const AIFunction = button.match('translate') ? `(You're not allowed to say anything like (The text is already in ${UniqueLangs[0]}"\nNo translation is needed).\Translate into ${Lang} the following text:\n"${Prompt}".\nAfter showing (in order) a few possible "Translations:" also give me a "Definition:" and "Examples:".You must answer using only 1 language first, then use only the other language, don't mix both languages! ` : button.match('Prompt') ? context : `(PS*I'm unable to provide you with more context, so don't ask for it! Also, don't mention that I haven't provided context or anything similar to it!) ${IsQuestion}: "${Prompt}"`; //AI prompts
    const msg = button.match('translate') ? `Translate this text: "${Prompt.length > 215 ? Prompt.trim().slice(0, 215) + '…' : Prompt.trim()}"` : button.match('Prompt') ? Prompt.length > 240 ? Prompt.trim().slice(0, 240) + '…' : Prompt.trim() : `Help me further explore a term or topic from the text: "${Prompt.length > 180 ? Prompt.trim().slice(0, 180) + '…' : Prompt.trim()}"`; //User text

    const request = GM.xmlHttpRequest({ //Call the AI API
      method: "POST",
      url: `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:streamGenerateContent?key=${GM_getValue("APIKey")}`,
      responseType: 'stream',
      headers: {
        "Content-Type": "application/json"
      },
      data: JSON.stringify({
        contents: [{
          parts: [{
            text: `${AIFunction}` //Use our AI prompt
          }]
        }],
        safetySettings: [ //Allow all content
          { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_NONE" },
          { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_NONE" },
          { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_NONE" },
          { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_NONE" }
        ],
      }),
      onerror: function(err) {
        shadowRoot.querySelector("#finalanswer").innerHTML = (html => BypassTT?.createHTML(html) || html)(`<br>Please copy and paste the error below:<br><a class="feedback" href="https://greasyfork.org/scripts/419825/feedback">Click here to report this bug</a><br><br> Prompt: ${Prompt}<br> Button: ${button}<br> Error: ${err}}<br><br><br>`);
      },
      onload: function(response) {
        shadowRoot.querySelector("#AIMenu").classList.add('show');
        shadowRoot.querySelector("#dictate").classList.add('show');
        shadowRoot.querySelector("#TopPause").classList.remove('show');
      },
      onabort: function(response) {
        shadowRoot.querySelector("#AIMenu").classList.add('show');
        shadowRoot.querySelector("#dictate").classList.add('show');
        shadowRoot.querySelector("#TopPause").classList.remove('show');
        shadowRoot.querySelector("#finalanswer").innerText = 'ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤResponse has been interrupted.';
      },
      onloadstart: function(test) {
        shadowRoot.querySelector("#prompt").focus();
        shadowRoot.querySelector("#msg").innerHTML = msg;
        shadowRoot.querySelector("#TopPause").classList.add('show');
        shadowRoot.querySelector("#AIMenu").classList.remove('show');
        shadowRoot.querySelector("#dictate").classList.remove('show');

        shadowRoot.querySelector("#copyAnswer").onclick = function() {
          shadowRoot.querySelector("#copyAnswer").style.display = 'none';
          shadowRoot.querySelector("#AnswerCopied").style.display = 'inline-flex';
          GM_setClipboard(shadowRoot.querySelector("#finalanswer").innerText.replace('ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ', ''));
          setTimeout(() => { //Return play BTN svg
            shadowRoot.querySelector("#copyAnswer").style.display = 'inline-flex';
            shadowRoot.querySelector("#AnswerCopied").style.display = 'none';
          }, 1000);
        };

        const reader = test.response.getReader();
        const decoder = new TextDecoder();
        var buffer = '', partialMarkdown = '';

        function readStream() {
          reader.read().then(({ value }) => {
            buffer += decoder.decode(value, { stream: true });
            var startIdx = 0;
            while (true) {
              const openBrace = buffer.indexOf('{', startIdx);
              if (openBrace === -1) break;
              var balance = 1, closeBrace = openBrace + 1;

              while (balance > 0 && closeBrace < buffer.length) {
                if (buffer[closeBrace] === '{') balance++;
                if (buffer[closeBrace] === '}') balance--;
                closeBrace++;
              }

              if (balance !== 0) break; //Incomplete JSON object

              const jsonString = buffer.substring(openBrace, closeBrace);
              const item = JSON.parse(jsonString);
              partialMarkdown += item.candidates[0].content.parts[0].text;

              const tempDiv = document.createElement('div');
              tempDiv.innerHTML = marked.parse(partialMarkdown);

              shadowRoot.querySelector("#finalanswer").innerHTML = '';
              shadowRoot.querySelector("#finalanswer").appendChild(tempDiv);
              startIdx = closeBrace;
            }
            buffer = buffer.substring(startIdx);
            readStream();
          });
        }

        readStream();

        shadowRoot.querySelector("#CloseOverlay").classList.add('show');
        shadowRoot.querySelector("#highlight_menu").classList.remove('show'); //Hide the mini menu on the page
        shadowRoot.querySelectorAll("#AIBox, .animated-border, #AIBox.AnswerBox").forEach(el => el.classList.add('show')); //Show the AI input and box
        getSelection().removeAllRanges(); //UnSelect the selected text so that if the user clicks on a previously selected text the menu won't show up again

        var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition;
        const recognition = new SpeechRecognition();
        recognition.interimResults = true; //Show partial results
        recognition.continuous = true; //Keep listening until stopped

        var transcript = ""; //Add words
        shadowRoot.querySelector("#CloseOverlay").onclick = function() {
          [...shadowRoot.querySelector("#finalanswer div").childNodes].slice(0, -1).forEach(node => node.remove()); //Reset the text content
          shadowRoot.querySelectorAll("#AIBox, .animated-border, #AIBox.AnswerBox").forEach(el => el.classList.remove('show')); //Hide the AI input and box
          this.classList.remove('show');
          recognition.stop(); //Stop recognizing audio
          speechSynthesis.cancel(); //Stop speaking
          request.abort(); //Abort any ongoing request
          if (shadowRoot.querySelector("#gemini").style.display === 'none') {
            shadowRoot.querySelector("#AddContext").remove(); //Return original prompt input styles
            shadowRoot.querySelector("#context").classList.remove('show');
            shadowRoot.querySelector("#prompt").placeholder = 'Enter your prompt to Gemini'; //Return default placeholder
          }
        };

        shadowRoot.querySelector("#TopPause").onclick = function() {
          shadowRoot.querySelector("#dictate").classList.add('show');
          shadowRoot.querySelector("#TopPause").classList.remove('show');
          request.abort();
        };

        recognition.onend = function() {
          isRecognizing = false;
          shadowRoot.querySelectorAll('.state1, .state2, .state3').forEach((state, index) => { //ForEach SVG animation state
            index.toString().match(/1|2/) ? state.style.display = 'none' : ''; //Show only the 1 state
            state.classList.remove('animate'+index); //Stop the voice recording animation
          });

          transcript !== '' ? Generate(transcript, shadowRoot.querySelector("#prompt").className) : shadowRoot.querySelector("#finalanswer").innerHTML = (html => BypassTT?.createHTML(html) || html)(`<br>No audio detected. Please try again or check your mic settings.ㅤㅤㅤㅤㅤㅤㅤㅤㅤ<br><br>`); //Call the AI API if audio has been detected or show an error message
        }; //Finish the recognition end event listener

        recognition.onresult = function(event) { //Handle voice recognition results
          transcript = ""; //Clear the transcript at the start of the event
          for (var i = 0; i < event.results.length; i++) { //For all transcript results
            transcript += event.results[i][0].transcript + ' '; //Concatenate all intermediate transcripts
          }
          shadowRoot.querySelector("#msg").innerText = transcript.length > 240 ? transcript.slice(0, 240) + '…' : transcript; //Display recognized words
        };

        shadowRoot.querySelector("#dictate").onclick = function() {
          if (isRecognizing) {
            recognition.stop();
          } else {
            isRecognizing = true;
            recognition.start();
            shadowRoot.querySelectorAll('.state1, .state2, .state3').forEach((state, index) => { //ForEach SVG animation state
              state.style.display = 'unset'; //Show all states
              state.classList.add('animate'+index); //Start the voice recording animation
            });
          }
        };

        speechSynthesis.onvoiceschanged = () => desiredVoice = speechSynthesis.getVoices().find(v => v.name === "Microsoft Zira - English (United States)"); //Get and store the desired voice
        speechSynthesis.onvoiceschanged(); //Handle cases where the event doesn't fire

        shadowRoot.querySelectorAll("#speak, #SpeakingPause").forEach(function(el) {
          el.onclick = function() { //When the speak or the bottom pause BTNs are clicked
            if (speechSynthesis.speaking) {
              speechSynthesis.cancel();
              shadowRoot.querySelector("#speak").style.display = 'inline-flex'; //Show the play BTN
              shadowRoot.querySelector("#SpeakingPause").classList.remove('show'); //Hide the pause BTN
            }
            else
            {
              shadowRoot.querySelector("#speak").style.display = 'none'; //Hide the play BTN
              shadowRoot.querySelector("#SpeakingPause").classList.add('show');

              var audio = new SpeechSynthesisUtterance(shadowRoot.querySelector("#finalanswer").innerText.replace(/\(?..-..\)?:?|[^a-zA-Z0-9\s%.,!?]/g, '')); //Play the AI response text, removing non-alphanumeric characters and lang locales for better pronunciation
              audio.voice = desiredVoice; //Use the desiredVoice
              speechSynthesis.speak(audio); //Speak the text

              audio.onend = (event) => {
                shadowRoot.querySelector("#speak").style.display = 'inline-flex'; //Show the play BTN
                shadowRoot.querySelector("#SpeakingPause").classList.remove('show');
              };
            }
          };
        });

        shadowRoot.querySelector("#NewAnswer").onclick = function() {
          recognition.stop(); //Stop recognizing audio
          speechSynthesis.cancel(); //Stop speaking
          shadowRoot.querySelector("#speak").style.display = 'inline-flex'; //Show the play BTN
          shadowRoot.querySelector("#SpeakingPause").classList.remove('show'); //Hide the pause BTN
          shadowRoot.querySelector("#dictate").classList.remove('show');
          shadowRoot.querySelector("#TopPause").classList.add('show');
          Generate(Prompt, button); //Call the AI API
        };
      } //Finishes the onloadstart event listener
    });//Finishes the GM.xmlHttpRequest function
  } //Finishes the Generate function

  shadowRoot.querySelector("#prompt").addEventListener("keydown", (event) => {
    if (event.key === "Enter") {
      Generate(shadowRoot.querySelector("#prompt").value, shadowRoot.querySelector("#prompt").className); //Call the AI API
      shadowRoot.querySelector("#prompt").value = ''; //Erase the prompt text
    }
    if (event.key === "Tab") {
      if (shadowRoot.querySelector("#prompt").placeholder.match('using')) { //If the input bar contains the word "using"
        shadowRoot.querySelector("#AddContext").remove(); //Return original prompt input styles
        shadowRoot.querySelector("#context").classList.remove('show'); //Hide the context view
        shadowRoot.querySelector("#prompt").placeholder = 'Enter your prompt to Gemini'; //Return default placeholder
      }
      else
      {
        shadowRoot.querySelector("#context").classList.add('show'); //Show the context view
        shadowRoot.querySelector("#prompt").placeholder = `Gemini is using ${location.host.replace('www.','')} for context...`; //Change placeholder
        shadowRoot.querySelector("#highlight_menu").insertAdjacentHTML('beforebegin', ` <style id="AddContext"> #gemini { display: none; } #prompt { left: 12%; width: 75%; } #tabcontext { display: none; } .animated-border { --color-OrangeORLilac: #FF8051; /* Change the border effect color to orange */ } </style> `); //Show the context bar
      }
    }
    setTimeout(() => { //Wait for the code above to execute
      shadowRoot.querySelector("#prompt").focus(); //Refocus on the input bar
    }, 0);
  });

  shadowRoot.querySelectorAll("#AIBTN").forEach(function(button) {
    button.onmousedown = function(event, i) { //When the Explore or the Translate BTNs are clicked
      if (GM_getValue("APIKey") === undefined || GM_getValue("APIKey") === null || GM_getValue("APIKey") === '') { //Set up the API Key if it isn't already set
        GM_setValue("APIKey", prompt('Enter your API key\n*Press OK\n\nYou can get a free API key at https://aistudio.google.com/app/apikey'));
      }
      if (GM_getValue("APIKey") !== null && GM_getValue("APIKey") !== '') {
        Generate(SelectedText, this.className); //Call the AI API
      }
    };
  });

  if (document.body.textContent !== '' || document.body.innerText !== '') //If the body has any text
  {
    document.body.appendChild(HtmlMenu); //Add the script menu div container
  }

  shadowRoot.querySelector('#CopyBTN').onmousedown = function() {
    GM_setClipboard(getSelection().toString());
  };

  window.addEventListener('scroll', async function() {
    shadowRoot.querySelector("#highlight_menu").classList.remove('show'); //Hide the menu
    if (LeftClicked === false && SelectedText !== '') { //If the Left Click isn't being held, and if something is currently selected
      getSelection().removeAllRanges(); //UnSelect the selected text when scrolling the page down so that if the user clicks on the past selected text the menu won't show up again
    }
  });
}