// ==UserScript== // @name AI Everywhere // @namespace OperaBrowserGestures // @description Highly customizable 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 Converters, all features can be easily modified or disabled. // @version 61 // @author hacker09 // @include * // @exclude https://accounts.google.com/v3/signin/* // @icon https://i.imgur.com/8iw8GOm.png // @grant GM_registerMenuCommand // @grant GM.xmlHttpRequest // @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 // @require https://cdnjs.cloudflare.com/ajax/libs/marked/13.0.2/marked.min.js // @downloadURL none // ==/UserScript== const BypassTT = window.trustedTypes?.createPolicy('BypassTT', { createHTML: HTML => HTML }); //Bypass trustedTypes if (GM_getValue("APIKey") === undefined || GM_getValue("APIKey") === null || GM_getValue("APIKey") === '') { //If the API Key isn't already set window.onload = function() { //Onload if (location.href === 'https://aistudio.google.com/app/apikey' && document.querySelector(".apikey-link") !== null) { //If the user is on the API Key site and already created an API Key setTimeout(function() { //Start the setTimeout function document.querySelectorAll(".apikey-link")[1].click(); //Click on the API Key setTimeout(function() { //Start the 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!'); //Show a message }, 500); //Finish the setTimeout function }, 500); //Finish the setTimeout function } //Finish the if condition }; //Finish the onload event listener } //Finish the if condition // Mouse Gestures _________________________________________________________________________________________________________________________________________________________ GM_registerMenuCommand("Enable/Disable Mouse Gestures", MouseGestures); //Adds an option to the tampermonkey menu if (GM_getValue("MouseGestures") !== true && GM_getValue("MouseGestures") !== false) { //If MouseGestures isn't already set GM_setValue("MouseGestures", true); //Enable the feature } //Finish the if condition function MouseGestures() //Enable/disable MouseGestures { //Start the function MouseGestures if (GM_getValue("MouseGestures") === true) { //If it's enabled, disable it GM_setValue("MouseGestures", false); //Disable the feature } //Finish the if condition else { //If it's disabled, enable it GM_setValue("MouseGestures", true); //Enable the feature location.reload(); //Reload the page } //Finish the else condition } //Finish the function MouseGestures if (GM_getValue("MouseGestures") === true) //If the MouseGestures is enabled { //Start the if condition const SENSITIVITY = 3; //Adjust the script mouse sensitivity here between 1 ~ 5 const TOLERANCE = 3; //Adjust the script mouse tolerance here between 1 ~ 5 const funcs = { //Store the MouseGestures functions 'L': function() { //Run when the mouse movement Left is performed window.history.back(); //Go Back }, //Finish the mouse movement Left 'R': function() { //Run when the mouse movement Right is performed window.history.forward(); //Go Forward }, //Finish the mouse movement Right 'D': function() { //Run when the mouse movement Down is performed if (IsShiftNotPressed === true) { //If the shift key isn't being pressed GM_openInTab(link, { //Open the link on a new tab active: true, //Focus on the new tab insert: true, //Insert the new tab after the actual tab setParent: true //When closed return to the previous tab }); //Open the link that was hovered } //Finish the if condition }, //Finish the mouse movement Down 'UD': function() { //Run when the mouse movement Up+Down is performed location.reload(); //Reload the Tab }, //Finish the mouse movement Up+Down 'DR': function(e) { //Run when the mouse movement Down+Right is performed top.close(); //Close the tab e.preventDefault(); //Disable the context menu e.stopPropagation(); //Disable the context menu }, //Finish the mouse movement Down+Right 'DU': function() { //Run when the mouse movement Down+Up is performed GM_openInTab(link, { //Open the link that was hovered active: false, //Don't focus on the new tab insert: true, //Insert the new tab after the actual tab setParent: true //Return to the tab the user was in }); //Open the link that was hovered on a new background tab } //Finish the mouse movement Down+Up }; //Finish the variable to store the functions //Math codes to track the mouse movement gestures const s = 1 << ((7 - SENSITIVITY) << 1); const t1 = Math.tan(0.15708 * TOLERANCE),t2 = 1 / t1; let x, y, path; const tracer = function(e) { //Start the const tracer let cx = e.clientX, cy = e.clientY, deltaX = cx - x, deltaY = cy - y, distance = deltaX * deltaX + deltaY * deltaY; if (distance > s) { let 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; } }; //Finish the const tracer window.addEventListener('mousedown', function(e) { //When the mouse is clicked if (e.which === 3) { //Start the if condition x = e.clientX; y = e.clientY; path = ""; window.addEventListener('mousemove', tracer, false); //Detect the mouse position } //Finish the if condition }, false); //Finish the event listener var IsShiftNotPressed = true; //Hold the shift key status window.addEventListener("contextmenu", function(e) { //When the shift key is/isn't pressed if (e.shiftKey) { //If the shift key was pressed IsShiftNotPressed = false; //Hold the shift key status open(link, '_blank', 'height=' + screen.height + ',width=' + screen.width); //Open the link on a new window } //Finish the if condition if (LeftClicked === true) { //If the Left Click was released when the Rocker Mouse Gestures were enabled e.preventDefault(); //Disable the context menu e.stopPropagation(); //Disable the context menu } //Finish the if condition setTimeout(function() { //Start the setTimeout function IsShiftNotPressed = true; //Hold the shift key status }, 500); //Finish the setTimeout function }, false); //Finish the event listener window.addEventListener('contextmenu', function(e) { //When the right click BTN is released window.removeEventListener('mousemove', tracer, false); //Track the mouse movements if (path !== "") { //Start the if condition e.preventDefault(); //Disable the context menu if (funcs.hasOwnProperty(path)) { //Start the if condition funcs[path](); } //Finish the if condition } //Finish the if condition }, false); //Finish the event listener var link; //Make the variable global Array.from(document.querySelectorAll('a')).forEach(Element => Element.onmouseover = function() { //Add an event listener to all link elements link = this.href; //Store the hovered link to a variable }); //Finish the forEach Array.from(document.querySelectorAll('a')).forEach(Element => Element.onmouseout = function() { //Add an event listener to all link elements const PreviousLink = link; //Save the hovered link to another variable setTimeout(function() { //Start the setTimeout function if (PreviousLink === link) //If the hovered link is still the same as the previously hovered Link { //Start the if condition link = 'about:newtab'; //Make the script open a new browser tab when the mouse leaves any link that was hovered } //Finish the if condition }, 200); //Finish the setTimeout function }); //Finish the forEach } //Finish the if condition //Rocker Mouse Gesture Settings _________________________________________________________________________________________________________________________________________________________ GM_registerMenuCommand("Enable/Disable Rocker Mouse Gestures", RockerMouseGestures); //Adds an option to the tampermonkey menu if (GM_getValue("RockerMouseGestures") !== true && GM_getValue("RockerMouseGestures") !== false) { //If RockerMouseGestures isn't already set GM_setValue("RockerMouseGestures", false); //Disable the feature } //Finish the if condition function RockerMouseGestures() //Enable/disable RockerMouseGestures { //Start the function RockerMouseGestures if (GM_getValue("RockerMouseGestures") === true) { //If it's enabled, disable it GM_setValue("RockerMouseGestures", false); //Disable the feature } //Finish the if condition else { //If it's disabled, enable it GM_setValue("RockerMouseGestures", true); //Enable the feature location.reload(); //Reload the page } //Finish the else condition } //Finish the function RockerMouseGestures if (GM_getValue("RockerMouseGestures") === true || GM_getValue("SearchHiLight") === true) //If the RockerMouseGestures or the SearchHiLight is enabled { //Start the if condition var LeftClicked, RightClicked; //Create global variables window.addEventListener("mousedown", function(e) { //Detect the right and left mouse clicks presses on the page switch (e.button) { //Start the switch condition case 0: //If Left Click was Pressed LeftClicked = true; //Set the variable LeftClicked as true break; case 2: //If Right Click was Pressed RightClicked = true; //Set the variable RightClicked as true break; } //Finish the switch condition }, false); //Finish the event listener mouse down window.addEventListener("mouseup", function(e) { //Detect the right and left mouse clicks releases on the page switch (e.button) { //Start the switch condition case 0: //If Left Click was released LeftClicked = false; //Set the variable LeftClicked as false break; case 2: //If Right Click was released RightClicked = false; //Set the variable RightClicked as false break; } //Finish the switch condition if (LeftClicked && RightClicked === false) { //If Left was Clicked and then Right Click was released history.back(); //Go Back } //Finish the if condition if (RightClicked && LeftClicked === false) { //If Right was Clicked and then Left Click was released history.forward(); //Go Forward } //Finish the if condition }, false); //Finish the event listener mouse up } //Finish the if condition //SearchHighLight + CurrenciesConverter + UnitsConverter _______________________________________________________________________________________________________________________________________ GM_registerMenuCommand("Enable/Disable SearchHiLight", SearchHiLight); //Adds an option to the tampermonkey menu if (GM_getValue("SearchHiLight") !== true && GM_getValue("SearchHiLight") !== false) { //If SearchHiLight isn't already set GM_setValue("SearchHiLight", true); //Enable the feature } //Finish the if condition if (GM_getValue("CurrenciesConverter") !== true && GM_getValue("CurrenciesConverter") !== false) { //If CurrenciesConverter isn't already set GM_setValue("CurrenciesConverter", true); //Enable the feature } //Finish the if condition if (GM_getValue("UnitsConverter") !== true && GM_getValue("UnitsConverter") !== false) { //If UnitsConverter isn't already set GM_setValue("UnitsConverter", true); //Enable the feature } //Finish the if condition function SearchHiLight() //Enable/disable the SearchHiLight and the Currency/Unit converters { //Start the function SearchHiLight if (GM_getValue("SearchHiLight") === true) { //If it's enabled, disable it GM_setValue("SearchHiLight", false); //Disable the feature GM_setValue("CurrenciesConverter", false); //Disable the feature GM_deleteValue('YourLocalCurrency'); //Remove the YourLocalCurrency variable off the tampermonkey storage GM_setValue("UnitsConverter", false); //Disable the feature } //Finish the if condition else { //If it's disabled, enable it GM_setValue("SearchHiLight", true); //Enable the feature if (confirm('If you want to enable the Currency Converter press OK.')) //Confirm to enable/disable feature { //Start the if condition GM_setValue("CurrenciesConverter", true); //Enable the feature } //Finish the if condition else //If the user presses cancel { //Start the else condition GM_setValue("CurrenciesConverter", false); //Disable the feature } //Finish the else condition if (confirm('If you want to enable the Units Converter press OK.')) //Confirm to enable/disable feature { //Start the if condition GM_setValue("UnitsConverter", true); //Enable the feature } //Finish the if condition else //If the user presses cancel { //Start the else condition GM_setValue("UnitsConverter", false); //Disable the feature } //Finish the else condition location.reload(); //Reload the page } //Finish the else condition } //Finish the function SearchHiLight if (GM_getValue("SearchHiLight") === true) //If the SearchHiLight is enabled { //Start the if condition var SelectedTextIsLink; //Create a global variable 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); //Create a global variable to check if the selected text is a link var FinalCurrency, SelectedText, SelectedTextSearch = ''; //Create global variables window.addEventListener('load', function() { //Start the function document.body.addEventListener('mouseup', function() { //When the user releases the mouse click after selecting something SelectedText = getSelection().toString(); //Store the selected text SelectedTextSearch = getSelection().toString().replaceAll('&', '%26'); //Store the selected text to be opened on Google //CurrenciesConverter _______________________________________________________________________________________________________________________________________ if (GM_getValue("CurrenciesConverter") === true) { //If Currencies Converter is enabled shadowRoot.querySelector("#ShowCurrencyORUnits").innerText = ''; //Remove the previous Currency text shadowRoot.querySelectorAll('.AI-BG-box button')[1].style.marginTop = '34%'; //Bring the Translate AI box to it's original position 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|CNY|CZK|DKK|EUR|EGP|ETH|GBP|GEL|HKD|HRK|HUF|IDR|ILS|INR|JPY|LTC|KRW|MXN|MYR|NOK|NZD|PHP|PLN|RON|RM|RUB|SEK|SGD|THB|TRY|USD|UAH|ZAR|KZT|YTL|\$|R\$|HK\$|US\$|\$US|¥|€|Rp|kn|Kč|kr|zł|£|฿|₩))(?:\1[ \t\xA0]*\2|\2[ \t\xA0]*\1)[ \t\xA0]*$/i); //Regex to check if the selected text is a currency if (SelectedText.match(Currencies) !== null) //If the selected text is a currency { //Start the if condition if (GM_getValue("YourLocalCurrency") === undefined) { //If the Local Currency hasn't been set var 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'); //Get the user input GM_setValue("YourLocalCurrency", UserInput); //Store the user local currency } //Finish the if condition (async () => { //Async function to get the final converted currency value var CurrencySymbol = SelectedText.match(Currencies)[2]; //Store the currency symbol supposing "it's correct" const CurrencySymbols = new RegExp(/\$|R\$|HK\$|US\$|\$US|¥|€|Rp|kn|Kč|kr|zł|£|฿|₩/i); //Create a variable to check for the selected currency symbols if (SelectedText.match(Currencies)[2].match(CurrencySymbols) !== null) //If the selected currency contains a symbol { //Start the if condition switch (SelectedText.match(CurrencySymbols)[0].toLowerCase()) { //If the selected currency contains a symbol case '$': //Get the selected currency symbol case 'us$': //Get the selected currency symbol case '$us': //Get the selected currency symbol CurrencySymbol = 'USD'; //"Convert" the symbol to the Currency Letters break; case 'r$': //Get the selected currency symbol CurrencySymbol = 'BRL'; //"Convert" the symbol to the Currency Letters break; case 'hk$': //Get the selected currency symbol CurrencySymbol = 'HKD'; //"Convert" the symbol to the Currency Letters break; case "¥": //Get the selected currency symbol CurrencySymbol = 'JPY'; //"Convert" the symbol to the Currency Letters break; case '€': //Get the selected currency symbol CurrencySymbol = 'EUR'; //"Convert" the symbol to the Currency Letters break; case 'rp': //Get the selected currency symbol CurrencySymbol = 'IDR'; //"Convert" the symbol to the Currency Letters break; case 'kn': //Get the selected currency symbol CurrencySymbol = 'HRK'; //"Convert" the symbol to the Currency Letters break; case 'kč': //Get the selected currency symbol CurrencySymbol = 'CZK'; //"Convert" the symbol to the Currency Letters break; case 'kr': //Get the selected currency symbol CurrencySymbol = 'DKK'; //"Convert" the symbol to the Currency Letters break; case 'zł': //Get the selected currency symbol CurrencySymbol = 'PLN'; //"Convert" the symbol to the Currency Letters break; case '£': //Get the selected currency symbol CurrencySymbol = 'GBP'; //"Convert" the symbol to the Currency Letters break; case '฿': //Get the selected currency symbol CurrencySymbol = 'THB'; //"Convert" the symbol to the Currency Letters break; case '₩': //Get the selected currency symbol CurrencySymbol = 'KRW'; //"Convert" the symbol to the Currency Letters break; } //Finish the switch condition } //Finish the if condition GM.xmlHttpRequest({ //Make a request to grab the final converted currency value method: "GET", url: `https://www.google.com/search?q=${SelectedText.match(Currencies)[1]} ${CurrencySymbol} in ${GM_getValue("YourLocalCurrency")}`, onload: (response) => { const newDocument = new DOMParser().parseFromString(response.responseText, 'text/html'); //Parses the fetch response const FinalCurrency = parseFloat(newDocument.querySelector(".SwHCTb").innerText.split(' ')[0].replaceAll(',', '')); //Store the FinalCurrency and erase all commas shadowRoot.querySelectorAll('.AI-BG-box button')[1].style.marginTop = '23%'; //Bring the Translate AI box to the top if (SelectedText.match(Currencies)[2].match(CurrencySymbols) !== null) { //If the selected currency contains a symbol shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(CurrencySymbol + ' 🠂 ' + Intl.NumberFormat(navigator.language, { style: 'currency', currency: GM_getValue("YourLocalCurrency") }).format(FinalCurrency) + ''); //Show the FinalCurrency } else { //If the selected currency contains no symbol shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(Intl.NumberFormat(navigator.language, { style: 'currency', currency: GM_getValue("YourLocalCurrency") }).format(FinalCurrency) + ''); //Show the FinalCurrency } var htmlcode = shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML; //Save the currency html shadowRoot.querySelector("#ShowCurrencyORUnits").onmousemove = function() { //When the mouse hovers the currency shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)('Copy'); //Change the element text to copy }; //Finish the onmousemove event listener shadowRoot.querySelector("#ShowCurrencyORUnits").onmouseout = function() { //When the mouse leaves the currency shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(htmlcode); //Return the previous converted currency html }; //Finish the onmouseout event listener shadowRoot.querySelector("#ShowCurrencyORUnits").onclick = function() { //When the user clicks on the currency shadowRoot.querySelectorAll('.AI-BG-box button')[1].style.marginTop = '23%'; //Bring the Translate AI box to the top navigator.clipboard.writeText(Intl.NumberFormat(navigator.language, { style: 'currency', currency: GM_getValue("YourLocalCurrency") }).format(FinalCurrency)); //Copy the Final Currency }; //Finish the onclick event listener } }); })(); //Finish the async function } //Finish the if condition } //Finish the if condition //UnitsConverter _________________________________________________________________________________________________________________________________________________________________________ if (GM_getValue("UnitsConverter") === true) { //If the Units Converter option is enabled shadowRoot.querySelectorAll('.AI-BG-box button')[1].style.marginTop = '34%'; //Bring the Translate AI box to its original position shadowRoot.querySelector("#ShowCurrencyORUnits").innerText = ''; //Remove the previous Units text const Units = new RegExp(/^[ \t\xA0]*(-?\d+(?:[., ]\d+)?)[ \t\xA0]*("|inch|inches|in|cms?|centimeters?|meters?|ft|kg|lbs?|pounds?|kilograms?|ounces?|g|ozs?|fl oz|fl oz (us)|fluid ounces?|kphs?|km\/h|kilometers per hours?|mphs?|meters per hours?|°?º?[CF]|km\/hs?|ml|milliliters?|l|liters?|litres?|gal|gallons?|yards?|yd|Millimeter|millimetre|kilometers?|mi|mm|miles?|km|ft|fl|feets?|mts?|grams?|kilowatts?|kws?|brake horsepower|mechanical horsepower|hps?|bhps?|miles per gallons?|mpgs?|liters per 100 kilometers?|l\/100km|liquid quarts?|lqs?|foot-?pounds?|ft-?lbs?|lb fts?|newton-?meters?|nm|\^\d+)[ \t\xA0]*(?:\(\w+\)[ \t\xA0]*)?$/i); //Create a variable to check if the selected text has units if (SelectedText.match(Units) !== null) //If the selected text is an unit { //Start the if condition const SelectedUnitValue = SelectedText.match(Units)[1].replaceAll(',', '.'); //Get the selected unit value and store the value to a variable switch (SelectedText.match(Units)[2].toLowerCase()) { //Set all letters to Lower Case and convert the selected unit case 'inch': //Get the unit type case 'inches': //Get the unit type case 'in': //Get the unit type case '"': //Get the unit type var NewUnit = 'cm'; //"Convert" the current unit var ConvertedUnit = parseFloat(SelectedUnitValue * 2.54).toFixed(2); //Store the converted unit result break; case 'centimeter': //Get the unit type case 'centimeters': //Get the unit type case 'cm': //Get the unit type case 'cms': //Get the unit type NewUnit = 'in'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue / 2.54).toFixed(2); //Store the converted unit result break; case 'mt': //Get the unit type case 'mts': //Get the unit type case 'meters': //Get the unit type case 'meter': //Get the unit type NewUnit = 'ft'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue * 3.281).toFixed(2); //Store the converted unit result break; case 'kg': //Get the unit type case 'kilograms': //Get the unit type case 'kilogram': //Get the unit type NewUnit = 'lb'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue * 2.205).toFixed(2); //Store the converted unit result break; case 'pound': //Get the unit type case 'pounds': //Get the unit type case 'lb': //Get the unit type case 'lbs': //Get the unit type NewUnit = 'kg'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue / 2.205).toFixed(2); //Store the converted unit result break; case 'ounce': //Get the unit type case 'ounces': //Get the unit type case 'oz': //Get the unit type case 'ozs': //Get the unit type NewUnit = 'g'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue * 28.35).toFixed(2); //Store the converted unit result break; case 'g': //Get the unit type case 'gram': //Get the unit type case 'grams': //Get the unit type NewUnit = 'oz'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue / 28.35).toFixed(2); //Store the converted unit result break; case 'kilometer': //Get the unit type case 'kilometers': //Get the unit type NewUnit = 'mi'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue / 1.609).toFixed(2); //Store the converted unit result break; case 'kph': //Get the unit type case 'kphs': //Get the unit type case 'km/h': //Get the unit type case 'km/hs': //Get the unit type case 'kilometers per hour': //Get the unit type case 'kilometers per hours': //Get the unit type NewUnit = 'mph'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue * 1000).toFixed(2); //Store the converted unit result break; case 'mph': //Get the unit type case 'mphs': //Get the unit type case 'meters per hour': //Get the unit type case 'meters per hours': //Get the unit type NewUnit = 'km/h'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue / 1.000).toFixed(2); //Store the converted unit result break; case 'mi': //Get the unit type case 'mile': //Get the unit type case 'miles': //Get the unit type NewUnit = 'km'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue * 1.609).toFixed(2); //Store the converted unit result break; case '°c': //Get the unit type NewUnit = '°F'; //"Convert" the current unit ConvertedUnit = parseFloat((SelectedUnitValue * 9 / 5) + 32).toFixed(2); //Store the converted unit result break; case '°f': //Get the unit type NewUnit = '°C'; //"Convert" the current unit ConvertedUnit = parseFloat((SelectedUnitValue - 32) * 5 / 9).toFixed(2); //Store the converted unit result break; case 'ºc': //Get the unit type NewUnit = 'ºF'; //"Convert" the current unit ConvertedUnit = parseFloat((SelectedUnitValue * 9 / 5) + 32).toFixed(2); //Store the converted unit result break; case 'ºf': //Get the unit type NewUnit = 'ºC'; //"Convert" the current unit ConvertedUnit = parseFloat((SelectedUnitValue - 32) * 5 / 9).toFixed(2); //Store the converted unit result break; case 'ml': //Get the unit type case 'milliliter': //Get the unit type case 'milliliters': //Get the unit type NewUnit = 'fl oz (US)'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue / 29.574).toFixed(2); //Store the converted unit result break; case 'fl oz (US)': //Get the unit type case 'fl oz': //Get the unit type case 'fl': //Get the unit type case 'fluid ounce': //Get the unit type case 'fluid ounces': //Get the unit type NewUnit = 'ml'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue * 29.574).toFixed(2); //Store the converted unit result break; case 'l': //Get the unit type case 'litre': //Get the unit type case 'liter': //Get the unit type case 'litres': //Get the unit type case 'liters': //Get the unit type NewUnit = 'gal (US)'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue / 3.785).toFixed(2); //Store the converted unit result break; case 'gal': //Get the unit type case 'gallon': //Get the unit type case 'gallons': //Get the unit type NewUnit = 'lt'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue * 3.785).toFixed(2); //Store the converted unit result break; case 'yard': //Get the unit type case 'yards': //Get the unit type case 'yd': //Get the unit type NewUnit = 'm'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue / 1.094).toFixed(2); //Store the converted unit result break; case 'mm': //Get the unit type case 'millimetre': //Get the unit type case 'Millimeters': //Get the unit type NewUnit = 'in'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue / 25.4).toFixed(2); //Store the converted unit result break; case 'ft': //Get the unit type case 'feet': //Get the unit type case 'feets': //Get the unit type NewUnit = 'mt'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue * 0.3048).toFixed(2); //Store the converted unit result break; case 'kw': //Get the unit type case 'kws': //Get the unit type case 'kilowatt': //Get the unit type case 'kilowatts': //Get the unit type NewUnit = 'mhp'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue * 1.341).toFixed(2); //Store the converted unit result break; case 'mhp': //Get the unit type case 'mhps': //Get the unit type case 'hp': //Get the unit type case 'hps': //Get the unit type case 'brake horsepower': //Get the unit type case 'mechanical horsepower': //Get the unit type NewUnit = 'kw'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue / 1.341).toFixed(2); //Store the converted unit result break; case 'mpg': //Get the unit type case 'mpgs': //Get the unit type case 'miles per gallon': //Get the unit type case 'miles per gallons': //Get the unit type NewUnit = 'l/100km'; //"Convert" the current unit ConvertedUnit = parseFloat(235.215 / SelectedUnitValue).toFixed(2); //Store the converted unit result break; case 'l/100km': //Get the unit type case 'liters per 100 kilometer': //Get the unit type case 'liters per 100 kilometers': //Get the unit type NewUnit = 'US mpg'; //"Convert" the current unit ConvertedUnit = parseFloat(235.215 / SelectedUnitValue).toFixed(2); //Store the converted unit result break; case 'lq': //Get the unit type case 'lqs': //Get the unit type case 'liquid quart': //Get the unit type case 'liquid quarts': //Get the unit type NewUnit = 'l'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue / 1.057).toFixed(2); //Store the converted unit result break; case 'liter': //Get the unit type case 'liters': //Get the unit type NewUnit = 'lqs'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue * 1.057).toFixed(2); //Store the converted unit result break; case 'foot-pound': //Get the unit type case 'foot-pounds': //Get the unit type case 'foot pound': //Get the unit type case 'foot pounds': //Get the unit type case 'ft-lbs': //Get the unit type case 'ft-lb': //Get the unit type case 'ft lbs': //Get the unit type case 'ft lb': //Get the unit type case 'lb ft': //Get the unit type case 'lb fts': //Get the unit type NewUnit = 'Nm'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue * 1.3558179483).toFixed(2); //Store the converted unit result break; case 'newton-meter': //Get the unit type case 'newton-meters': //Get the unit type case 'newton meter': //Get the unit type case 'newton meters': //Get the unit type case 'nm': //Get the unit type NewUnit = 'ft lb'; //"Convert" the current unit ConvertedUnit = parseFloat(SelectedUnitValue / 1.3558179483).toFixed(2); //Store the converted unit result break; case (SelectedText.match(Units)[2].replaceAll(',', '.').match(/\^/)).input: //Get the unit type NewUnit = 'power'; //"Convert" the current unit ConvertedUnit = Math.pow(parseFloat(SelectedUnitValue), parseFloat(SelectedText.split('^')[1])); //Store the converted unit result break; } //Finish the switch condition setTimeout(function() { //Start the setTimeout function shadowRoot.querySelectorAll('.AI-BG-box button')[1].style.marginTop = '23%'; //Bring the Translate AI box to the top shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(ConvertedUnit + ' ' + NewUnit + ' '); //Show the converted unit results var htmlcode = shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML; //Save the converted unit value shadowRoot.querySelector("#ShowCurrencyORUnits").onmousemove = function() { //When the mouse hovers the unit shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)('Copy'); //Change the element text to copy }; //Finish the onmousemove event listener shadowRoot.querySelector("#ShowCurrencyORUnits").onmouseout = function() { //When the mouse leaves the unit shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(htmlcode); //Return the previous html }; //Finish the onmouseout event listener shadowRoot.querySelector("#ShowCurrencyORUnits").onclick = function() { //When the unit is clicked shadowRoot.querySelectorAll('.AI-BG-box button')[1].style.marginTop = '23%'; //Bring the Translate AI box to the top navigator.clipboard.writeText(ConvertedUnit); //Copy the Final converted unit value }; //Finish the onclick event listener }, 0); //Finish the setTimeout } //Finish the if condition } //Finish the if condition //Menu ___________________________________________________________________________________________________________________________________________________________________________ if (shadowRoot.querySelector("#SearchBTN").innerText === 'Open') //If the Search BTN text is 'Open' { //Start the if condition shadowRoot.querySelector("#highlight_menu > ul").style.paddingInlineStart = '19px'; //Increase the menu size shadowRoot.querySelector("#SearchBTN").innerText = 'Search'; //The next time that the menu is shown display the BTN text as Search again shadowRoot.querySelector("#OpenAfter").remove(); //Remove custom Open white hover overlay SelectedTextIsLink = false; //Make common words searchable again } //Finish the if condition if (SelectedText.match(Links) !== null) //If the selected text is a link { //Start the if condition SelectedTextIsLink = true; //Declare that it's a link shadowRoot.querySelector("#highlight_menu > ul").style.paddingInlineStart = '27px'; //Increase the menu size shadowRoot.querySelector("#SearchBTN").innerText = 'Open'; //Change the BTN text to Open shadowRoot.innerHTML += ` `; //Add custom Open white hover overlay } //Finish the if condition const menu = shadowRoot.querySelector("#highlight_menu"); //Store menu element if (document.getSelection().toString().trim() !== '') { //If text has been selected const p = document.getSelection().getRangeAt(0).getBoundingClientRect(); //Store the positions menu.style.display = ''; //Show the menu menu.offsetHeight; //Trigger reflow by forcing a style calculation menu.style.left = p.left + (p.width / 2) - (menu.offsetWidth / 2) + 'px'; //Position menu horizontally menu.style.top = p.top - menu.offsetHeight - 10 + 'px'; //Position menu vertically menu.classList.add('highlight_menu_animate'); //Add animation class return; //Exit if text is selected } menu.style.display = 'none'; //Hide the menu }); //Finish the mouse up event listener }); //Finish the function //AI Menu ___________________________________________________________________________________________________________________________________________________________________________ var audio, Generating, isRecognizing = false; //Declare global variables const HtmlMenu = document.createElement('div'); //Create a container div HtmlMenu.setAttribute('style', `width: 0px; height: 0px; display: block;`); //Hide the container div by default const shadowRoot = HtmlMenu.attachShadow({ mode: 'closed' }); //Create a closed shadow const BGColor = window.matchMedia && window.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 colors to white const TextColor = BGColor === '#e7edf1' ? 'black' : 'white'; //According to 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 = ' '; //Store the GeminiSVG shadowRoot.innerHTML = (html => BypassTT?.createHTML(html) || html)(` `); //Set the menu html shadowRoot.querySelector("#AIBTN:first-of-type").classList.add('show-button'); //Animate the Explore BTN shadowRoot.querySelector("#AIBTN.translate").classList.add('show-button'); //Animate the Translate BTN function RemoveContext() { //Start the function shadowRoot.querySelector("#gemini").style.display = ''; //Show the Gemini BTN shadowRoot.querySelector("#prompt").placeholder = 'Enter your prompt to Gemini'; //Return default placeholder shadowRoot.querySelector("#prompt").style.left = ''; //Return the input bar to its original left position shadowRoot.querySelector("#prompt").style.width = '64%'; //Return the input bar to its original width position shadowRoot.querySelector("#context").style.display = 'none'; //Hide the context view shadowRoot.querySelector("#tabcontext").style.display = 'flex'; //Show the "page context tab" shadowRoot.querySelector('.animated-border').style.setProperty('--color-OrangeORLilac', '#B969CC'); //Return default lilac border effect color } //Finish the function function Generate(Prompt, button) { //Call the AI endpoint const context = shadowRoot.querySelector("#context").style.display === '' ? `(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 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/word inside quotes "${Prompt}".\nAlso give me a definition and usage examples.` : 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!) Help me further explore a term or topic from the text/word: "${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 function startGeneratingText() { //Start the startGeneratingText function Generating = setInterval(function() { //Start the interval to change the text if (shadowRoot.querySelector("#finalanswer").innerText === 'ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ▋') { //Start the if condition shadowRoot.querySelector("#finalanswer").innerText = 'ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ'; //Remove ▋ } else { //Toggle between showing and hiding ▋ shadowRoot.querySelector("#finalanswer").innerText = 'ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ▋'; //Add ▋ } //Finish the else condition }, 200); //Finish the setInterval } //Finish the startGeneratingText function const request = GM.xmlHttpRequest({ //Call the AI API method: "POST", url: `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${GM_getValue("APIKey")}`, 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) { //Onerror clearInterval(Generating); //Stop showing ▋ shadowRoot.querySelector("#finalanswer").innerHTML = `
Please copy and paste the error below:
Click here to report this bug

Prompt: ${Prompt}
Button: ${button}
Error: ${err}}


`; //Show the error message }, onload: function(response) { //Onload clearInterval(Generating); //Stop showing ▋ const AIResponse = JSON.parse(response.responseText).candidates?.[0]?.content?.parts?.[0]?.text; if (AIResponse !== undefined) { //If AIResponse is not undefined shadowRoot.querySelector("#finalanswer").innerHTML = (html => BypassTT?.createHTML(html) || html)(marked.parse(AIResponse) + '
'); //Show the parsed AI response } else { //If AIResponse is undefined shadowRoot.querySelector("#finalanswer").innerHTML = `
Please copy and paste the error below:
Click here to report this bug

Prompt: ${Prompt}
Button: ${button}
Error: ${response.responseText}


`; //Show the error message } //Finish the else condition audio = new SpeechSynthesisUtterance(AIResponse.replace(/[^a-zA-Z0-9\s%.,!?]/g, '')); //Play the AI response text, removing non-alphanumeric characters for better pronunciation shadowRoot.querySelector("#copyAnswer").onclick = function() { //When the copy AI answer BTN is clicked shadowRoot.querySelector("#copyAnswer").style.display = 'none'; //Hide the copy AI answer BTN shadowRoot.querySelector("#AnswerCopied").style.display = 'inline-flex'; //Show the copied checkmark navigator.clipboard.writeText(AIResponse.replace(/(\*\*|##)/g, '')); //Copy the AI response without duplicated symbols setTimeout(() => { //Start the setTimeout function shadowRoot.querySelector("#copyAnswer").style.display = 'inline-flex'; //Show the copy BTN shadowRoot.querySelector("#AnswerCopied").style.display = 'none'; //Hide the copied checkmark }, 1000); //Finish the setTimeout function }; shadowRoot.querySelector("#dictate").style.display = ''; //Show the dictate BTN shadowRoot.querySelector("#TopPause").style.display = 'none'; //Hide the TopPause BTN shadowRoot.querySelector("#AIMenu").style.display = 'flex'; //Show the AIMenu BTN shadowRoot.querySelector("#prompt").focus(); //Focus on the input bar }, onabort: function(response) { //Onabort clearInterval(Generating); //Stop showing ▋ shadowRoot.querySelector("#finalanswer").innerText = 'ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤResponse has been interrupted.'; //Show a message shadowRoot.querySelector("#dictate").style.display = ''; //Show the dictate BTN shadowRoot.querySelector("#copyAnswer").style.display = 'none'; //Hide the copy AI answer BTN shadowRoot.querySelector("#TopPause").style.display = 'none'; //Hide the TopPause BTN shadowRoot.querySelector("#AIMenu").style.display = 'flex'; //Show the AIMenu BTN shadowRoot.querySelector("#speak").style.display = 'none'; //Hide the speak BTN }, onloadstart: function(response) { //Onloadstart shadowRoot.querySelector("#AIAnswer").innerHTML = (html => BypassTT?.createHTML(html) || html)(`
${msg}
${GeminiSVG}
`); //Create the AI menu HTML var transcript = ""; //Add words startGeneratingText(); //Call the function startGeneratingText shadowRoot.querySelector("#loadingScreen").style.display = ''; //Show the black background var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition; //Define SpeechRecognition var recognition = new SpeechRecognition(); //Create a new instance of the SpeechRecognition object recognition.interimResults = true; //Show partial results recognition.continuous = true; //Keep listening until stopped shadowRoot.querySelector("#highlight_menu").style.display = 'none'; //Hide the mini menu on the page shadowRoot.querySelectorAll("#AIBox").forEach(el => el.style.display = ''); //Show the AI input and box getSelection().removeAllRanges(); //UnSelect the selected text so that if the user clicks on the past selected text the menu won't show up again shadowRoot.querySelector("#loadingScreen").onclick = function() { //When the black background is clicked shadowRoot.querySelectorAll("#AIBox").forEach(el => el.style.display = 'none'); //Hide the AI input and box this.style.display = 'none'; //Hide the black background recognition.stop(); //Stop recognizing audio speechSynthesis.cancel(); //Stop speaking request.abort(); //Abort any ongoing request if (shadowRoot.querySelector("#gemini").style.display === 'none') { //If the Gemini BTN is hidden RemoveContext(); //Return original prompt input styles } //Finish the if condition } //Finish the onclick event listener shadowRoot.querySelector("#TopPause").onclick = function() { //When the TopPause BTN is clicked shadowRoot.querySelector("#dictate").style.display = '' //Show the dictate BTN shadowRoot.querySelector("#TopPause").style.display = 'none' //Hide the TopPause BTN request.abort(); //Abort the request } //Finish the onclick event listener recognition.onend = function() { //After the recognition ends 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); //Apply animation class }); //Finish the ForEach loop isRecognizing = false; //Update the flag transcript !== '' ? Generate(transcript, shadowRoot.querySelector("#prompt").className) : shadowRoot.querySelector("#finalanswer").innerHTML = `
It looks like there was no audio detected. Please try again or check your mic settings.

`; //Show the error message; //Call the AI API if audio has been detected }; //Finish the recognition end event listener recognition.onresult = function(event) { //Handle the result event 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 } //Finish the for loop shadowRoot.querySelector("#msg").innerText = transcript.length > 240 ? transcript.slice(0, 240) + '…' : transcript; //Display the recognized words }; //Finish the recognition result event listener shadowRoot.querySelector("#dictate").onclick = function() { //When dictate is clicked if (isRecognizing) { //If already recognizing recognition.stop(); //Stop recognizing } else { //If not recognizing isRecognizing = true; //Update the flag recognition.start(); //Start recognizing shadowRoot.querySelectorAll('.state1, .state2, .state3').forEach((state, index) => { //ForEach SVG animation state state.style.display = 'unset'; //Show all states state.classList.add('animate'+index); //Apply animation class }); //Finish the ForEach loop } //Finish the else condition }; //Finish the onclick event listener var desiredVoice = null; //Create a new null variable 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 function speakText(text) { //Create a function to speak the text audio.voice = desiredVoice; //Use the desiredVoice speechSynthesis.speak(audio); //Speak the text } //Finish the function shadowRoot.querySelectorAll("#speak, #bottompause").forEach(function(el) { //ForEach BTN el.onclick = function() { //When speak or the bottom pause BTNs are clicked if (speechSynthesis.speaking) { //If speech synthesis is speaking speechSynthesis.cancel(); //Stop speaking shadowRoot.querySelector("#speak").style.display = 'inline-flex'; //Show the play BTN shadowRoot.querySelector("#bottompause").style.display = 'none'; //Hide the pause BTN } //Finish the if condition else //If speech synthesis isn't speaking { //Start the else condition shadowRoot.querySelector("#speak").style.display = 'none' //Hide the play BTN shadowRoot.querySelector("#bottompause").style.display = 'inline-flex'; //Show the pause BTN speakText(audio); //Speak the AI reponse text audio.onend = (event) => { //When the audio ends shadowRoot.querySelector("#speak").style.display = 'inline-flex'; //Show the play BTN shadowRoot.querySelector("#bottompause").style.display = 'none'; //Hide the pause BTN }; //Finish the audio end event listener } //Finish the else condition }; //Finish the onclick event listener }); //Finish the ForEach loop shadowRoot.querySelector("#NewAnswer").onclick = function() { //When the user clicks on the new answer shadowRoot.querySelector("#dictate").style.display = 'none'; //Hide the dictate BTN shadowRoot.querySelector("#TopPause").style.display = 'inline-flex'; //Show the top pause BTN Generate(Prompt, button); //Call the AI API }; //Finish the onclick event listener } //Finish the onloadstart event listener });//Finish the GM.xmlHttpRequest function } //Finish the Generate function shadowRoot.querySelector("#prompt").addEventListener("keydown", (event) => { //When the user presses a key on the input bar if (event.key === "Enter") { //If enter is pressed Generate(shadowRoot.querySelector("#prompt").value, shadowRoot.querySelector("#prompt").className); //Call the AI API shadowRoot.querySelector("#prompt").value = ''; //Erase the prompt text } //Finish the if condition if (event.key === "Tab") { //If tab is pressed if (shadowRoot.querySelector("#gemini").style.display === 'none') { //If the Gemini BTN is hidden RemoveContext(); //Return original prompt input styles } //Finish the if condition else //If tab the Gemini BTN isn't hidden { //Start the else condition shadowRoot.querySelector("#gemini").style.display = 'none'; //Hide the Gemini BTN shadowRoot.querySelector("#prompt").placeholder = `Gemini is using ${location.host.replace('www.','')} for context...`; //Change placeholder shadowRoot.querySelector("#prompt").style.left = '12%'; //Push the input bar to the left shadowRoot.querySelector("#prompt").style.width = '75%'; //Increase the input bar width shadowRoot.querySelector("#context").style.display = ''; //Show the context view shadowRoot.querySelector("#tabcontext").style.display = 'none'; //Hide the "page context tab" shadowRoot.querySelector('.animated-border').style.setProperty('--color-OrangeORLilac', '#FF8051'); //Change the border effect color to orange } //Finish the else condition } //Finish the if condition setTimeout(() => { //Wait for the code above to execute shadowRoot.querySelector("#prompt").focus(); //Refocus on the input bar }, 0); //Finish the setTimeout }); //Finish the keydown event listener if (document.body.textContent !== '' || document.body.innerText !== '') //If the body has any text { //Start the if condition document.body.appendChild(HtmlMenu); //Add the script menu div container } //Finish the if condition shadowRoot.querySelector("#SearchBTN").onmousedown = function() { //When the Search BTN is clicked var LinkfyOrSearch = 'https://www.google.com/search?q='; //Create a variable to open google if (SelectedTextIsLink === true) //If the selected text is a link { //Start the if condition LinkfyOrSearch = 'https://'; //Make the non-HTTP and non-HTTPS links able to be opened } //Finish the if condition if (SelectedText.match(/http:|https:/) !== null) //If the selected text is a link that already has HTTP or HTTPS { //Start the if condition LinkfyOrSearch = ''; //Remove the https:// that was previously added to this variable } //Finish the if condition GM_openInTab(LinkfyOrSearch + SelectedTextSearch, { //Open google and search for the selected text active: true, //Make the new tab active (selected) setParent: true, //Consider the new tab as a child of the current tab loadInBackground: true //Don't load the tab in the background }); //Finish the GM_openInTab function 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").style.display = 'none'; //Hide the menu }; //Finish the onmousedown event listener shadowRoot.querySelector("#CopyBTN").onmousedown = function() { //When the copy BTN is clicked navigator.clipboard.writeText(SelectedText); //Copy the selected text }; //Finish the onmousedown event listener shadowRoot.querySelectorAll("#AIBTN").forEach(function(button) { //For the Explore and Translate BTNs button.onmousedown = function(event,i) { //When the BTNs are Clicked if (GM_getValue("APIKey") === undefined || GM_getValue("APIKey") === null || GM_getValue("APIKey") === '') { //If the API Key isn't already set GM_setValue("APIKey", prompt('Enter your API key\n*Press OK')); //Store the API Key } //Finish the if condition if (GM_getValue("APIKey") !== null && GM_getValue("APIKey") !== '') { //if the API Key is already set Generate(SelectedText, this.className); //Call the AI API } //Finish the if condition }; //Finish the onmousedown event listener }); //Finish the foreach loop //Allow the script in iframes__________________________________________________________________________________________________________________________________________________________________ setTimeout(function() { //Start the setTimeout function const AllIframes = document.querySelectorAll("iframe"); //Get all iframes on the page for (var i = AllIframes.length; i--;) { //Start the for condition if (AllIframes[i].allow.match('clipboard-write;') === null && AllIframes[i].src.match(Links) !== null && AllIframes[i].src.match(/recaptcha|rt.*.*.edu|challenges.cloudflare|youtube|dailymotion|vimeo|streamtape|mcloud|vidstream|mp4upload|googlevideo|kaltura|crunchyroll|animesup|google.com\/recaptcha\/|blank.html|\.mp4/) === null) //If the iframe doesn't have the clipboard-write attribute yet, it has a link and it isn't a video { //Start the if condition AllIframes[i].allow = AllIframes[i].allow + 'clipboard-write;'; //Add the permission to copy the iframe text AllIframes[i].src = AllIframes[i].src; //Reload the iframe to apply the new permissions } //Finish the if condition } //Finish the for condition }, 4000); //Finish the setTimeout function window.addEventListener('scroll', async function() { //When the page is scrolled shadowRoot.querySelector("#highlight_menu").style.display = 'none'; //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 } //Finish the if condition }); //Finish the on-scroll event listener } //Finish the if condition