Skip to content

How to execute a user script before a page is saved

Gildas edited this page Oct 10, 2024 · 38 revisions
  1. When using SingleFile as:
  • an extension, enable the hidden option userScriptEnabled by exporting the settings from the options page, editing the JSON file, replacing userScriptEnabled: false with userScriptEnabled: true, and importing the modified file in SingleFile. You will also need to install an extension to store and run user scripts (search "user script" on stores).
  • a CLI tool, use the option --browser-script to pass script path(s) to SingleFile.
  1. Dispatch the custom event single-file-user-script-init in the user script.
dispatchEvent(new CustomEvent("single-file-user-script-init"));
  1. Listen to the custom event single-file-on-before-capture-request in the user script. The listener function is called just before the page is saved.
addEventListener("single-file-on-before-capture-request", () => {
  console.log("The page will be saved by SingleFile");
});

If the listener function has to be async, call event.preventDefault() and dispatch the custom event single-file-on-before-capture-response once the asynchronous tasks are terminated.

addEventListener("single-file-on-before-capture-request", async event => {
  event.preventDefault();
  try {
    await ...
  } finally {
    dispatchEvent(new CustomEvent("single-file-on-before-capture-response"));
  }
});

You can also get and set the options via the detail property of the single-file-on-before-capture-request and single-file-on-before-capture-response events. If the detail value is tainted (e.g. in Firefox), you should dispatch the single-file-user-script-init event with { detail: "jsonDetail" } as option. Then, you have to handle the detail value as a JSON string (instead of an object).

  1. Listen to the custom event single-file-on-after-capture-request in the user script to execute a script after the page is processed by SingleFile.
addEventListener("single-file-on-after-capture-request", () => {
  console.log("The page has been processed by SingleFile");
});

Like single-file-on-before-capture-request, you can call event.preventDefault() and dispatch the custom event single-file-on-after-capture-response if the listener is an async function.

  1. Examples
  • This script removes images from the page just before saving it and restores them after the page is processed.
// ==UserScript==
// @name         Remove images
// @namespace    https://github.com/gildas-lormeau/SingleFile
// @version      1.0
// @description  [SingleFile] Remove all the images
// @author       Gildas Lormeau
// @match        *://*/*
// @grant        none
// ==/UserScript==


(() => {

  const elements = new Map();
  const removedElementsSelector = "img";
  dispatchEvent(new CustomEvent("single-file-user-script-init"));

  addEventListener("single-file-on-before-capture-request", () => {
    document.querySelectorAll(removedElementsSelector).forEach(element => {
      const placeHolderElement = document.createElement(element.tagName);
      elements.set(placeHolderElement, element);
      element.parentElement.replaceChild(placeHolderElement, element);
    });
  });

  addEventListener("single-file-on-after-capture-request", () => {
    Array.from(elements).forEach(([placeHolderElement, element]) => {
      placeHolderElement.parentElement.replaceChild(element, placeHolderElement);
    });
    elements.clear();
  });

})();
  • This script converts the title to kebab-case and restores it after the page is processed.
// ==UserScript==
// @name         Convert title to kebab-case
// @namespace    https://github.com/gildas-lormeau/SingleFile
// @version      1.0
// @description  [SingleFile] Convert title to kebab-case
// @author       Gildas Lormeau
// @match        *://*/*
// @noframes
// @grant        none
// ==/UserScript==


(() => {

  let title;
  dispatchEvent(new CustomEvent("single-file-user-script-init"));

  addEventListener("single-file-on-before-capture-request", () => {
    title = document.title;
    document.title = document.title
      .replace(/[^a-z0-9-\s]/gi, "")
      .replace(/[-\s]+/g, "-")
      .toLowerCase();
  });

  addEventListener("single-file-on-after-capture-request", () => {
    document.title = title;
  });

})();