Sleazy Fork is available in English.

RedGIFs iframe Sound Helper

Provide facilities to automatically enable RedGIFs audio in embedded iframes, and for communication regarding audio controls between the the hosting site and the iframe.

// ==UserScript==
// @name     RedGIFs iframe Sound Helper
// @description    Provide facilities to automatically enable RedGIFs audio in embedded iframes, and for communication regarding audio controls between the the hosting site and the iframe.
// @license public domain
// @version  1.4.1
// @grant    none
// @include  https://www.redgifs.com/ifr/*
// @namespace https://greasyfork.org/users/1258441
// ==/UserScript==

const SOUND_DEFAULT = false; // false = off, true = on
const LINK_DEFAULT = true; // false = link removed, true = link remains

const urlParams = new URLSearchParams(window.location.search);

function waitForElm(selector) {
  // Lovingly stolen from Yong Wang and sashaolm on StackOverflow
  return new Promise(resolve => {
    if (document.querySelector(selector)) {
      return resolve(document.querySelector(selector));
    }
    const observer = new MutationObserver(mutations => {
      if (document.querySelector(selector)) {
        observer.disconnect();
        resolve(document.querySelector(selector));
      }
    });

    // If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336
    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  });
}

function toBoolean(argument) {
  if (argument === "true" || argument === "yes" || argument === "on" || argument === "1") {
    return true;
  }
  if (argument === "false" || argument === "no" || argument === "off" || argument === "0") {
    return false;
  }
  return undefined; // I could probably do this implicitly...
}

// Quick and dirty function to click an arbitrary element—not sure if elm.click() would work better?
var click = function(elm) {
  var clickEvent = new MouseEvent("click", {
    "bubbles": true,
    "cancelable": false,
    "view": window
  });
  elm.dispatchEvent(clickEvent);
};

// Facilities for enabling or disabling click-to-open link on the video
var saved_link = null;

function disableHyperLink(hyperlink) {
  if (hyperlink.tagName !== "A") {
    return;
  }
  if (saved_link === null) {
    saved_link = hyperlink.href;
  }
  hyperlink.href = "javascript: void(0)";
  hyperlink.target = "_self";
}

function enableHyperLink(hyperlink) {
  if (hyperlink.tagName !== "A" || saved_link === null) {
    return;
  }
  hyperlink.href = saved_link;
  hyperlink.target = "_blank";
}

// Facilities for turning looping on and off

var video_end_listener = (event) => window.parent.postMessage("gfy_ended", "*")

function disableLoop(video) {
  if (video.tagName !== "VIDEO") {
    return;
  }
  video.removeAttribute("loop");
  video.addEventListener("ended", function(event) {window.dispatchEvent(event);});
  video.addEventListener("ended", video_end_listener);
}

function enableLoop(video) {
  if (video.tagName !== "VIDEO") {
    return;
  }
  video.setAttribute("loop", "");
  video.removeEventListener("ended", video_end_listener);
}

// Setup configuration
const query_sound = toBoolean(urlParams.get("sound"));
const query_link = toBoolean(urlParams.get("link"));
const query_loop = toBoolean(urlParams.get("loop"));
const hash = window.location.hash;

var autoenable_sound = undefined;
var autodisable_link = undefined;
var autodisable_loop = undefined;

// Figure out whether to enable sound
if (query_sound === true) {
  autoenable_sound = true;
}
else if (query_sound === false) { // do nothing if it's undefined
  autoenable_sound = false;
}

if (window.location.hash === "#sound") {
  autoenable_sound = true;
}
else if (window.location.hash === "#nosound") {
  autoenable_sound = false;
}

if (autoenable_sound === undefined) {
  autoenable_sound = SOUND_DEFAULT;
}

// Figure out whether to disable the link
if (query_link === true) {
  autodisable_link = false;
}
else if (query_link === false) {
  autodisable_link = true;
}

if (autodisable_link === undefined) {
  autodisable_link = !LINK_DEFAULT;
}

// Figure out whether to disable looping
autodisable_loop = !query_loop; // loop should be ON by default

// Execute the above settings
if (autoenable_sound) {
  waitForElm(".soundOff").then(click);
}

if (autodisable_link) {
  waitForElm(".videoLink").then(disableHyperLink)
}

if (autodisable_loop) {
  waitForElm("video").then(disableLoop);
}

window.parent.postMessage("gfy_enhanced_api", "*");

// For communication with the parent window
window.onmessage = function(message) {
  const soundButton = document.querySelector(".soundOff") || document.querySelector(".soundOn");
  const loaded = soundButton !== null;

  switch (message.data) {
    case "soundOn":
      /* If the page is not loaded yet and sound will be off when it loads, wait for it to load then click .soundOff
       * If the page is not loaded yet and sound will be on when it loads, do nothing
       * If the page is loaded, look for .soundOff and click if present
       *
       * I worked it out, this is the only way to make it work.
       */
      if (loaded) {
        let button = document.querySelector(".soundOff");
        if (button !== null)
          click(button);
      }
      else if (!autoenable_sound)
        waitForElm(".soundOff").then(click);
      break;

    case "soundOff":
      /* If the page is not loaded yet and sound will be off when it loads, do nothing
       * If the page is not loaded yet and sound will be on when it loads, wait for it to load then click .soundOn
       * If the page is loaded, look for .soundOn and click if present
       */
      if (loaded) {
        let button = document.querySelector(".soundOn");
        if (button !== null)
          click(button);
      }
      else if (autoenable_sound)
        waitForElm(".soundOff").then(click);
      break;

    case "soundToggle":
      click(soundButton);
      break;

    case "linkOff":
      waitForElm(".videoLink").then(disableHyperLink);
      break;

    case "linkOn":
      waitForElm(".videoLink").then(enableHyperLink);
      break;

    case "loopOff":
      waitForElm("video").then(disableLoop);
      break;

    case "loopOn":
      waitForElm("video").then(enableLoop);
      break;

    case "pause":
      waitForElm("video").then((vid) => vid.pause());
      break;

    case "play":
      waitForElm("video").then((vid) => vid.play());
      break;

    default:
      console.error("Unknown command " + message.data);
  }
}