Search code examples
javascripthtmlgreasemonkeytampermonkey

Dynamically add new UserScript via an HTML button


I'm making a website to post some scripts that I made, I would like to create a button on my page to dynamically install the UserScript with Tampermonkey or Greasemonkey, just like in Greasyfork.

Page example: Script example


Solution

  • You can try to trigger a download using a hidden <a> element with a download attribute set to the filename ending in .user.js. You might need to enable some settings in your browser to detect downloading opening of UserScripts.

    Note: You may need to send the text to a server (PHP, Node, etc...), save the file as *.user.js and then create an <iframe> with an href poiting to the filename on the server to trigger a proper file download/install. When you install a script on Greasyfork, it downloads a *.user.js file that is hosted on their server(s). The filename that is downloaded fits the following the pattern:

    https://greasyfork.org/scripts/<ID>-script-name/code/Script%20Name.user.js

    Downloading scripts from the client or local file system is frowned upon in modern web dev.

    const download = (content, mimeType, filename) => {
      const
        a = document.createElement('a'),
        blob = new Blob([content], {type: mimeType}),
        url = URL.createObjectURL(blob);
      a.setAttribute('href', url);
      a.setAttribute('download', filename);
      a.click();
      console.log(`Downloading: ${filename}`);
    };
    
    const extractFilenameFromScript = (script) => {
      const [, scriptName] = script.match(/^\/\/\s*@name\s+(.+)$/m);
      return `${scriptName.replace(/[^a-z0-9]+/ig, '-').toLowerCase()}.user.js`;
    };
    
    const downloadScript = () => {
      const script = document.querySelector('.user-script').textContent.trim();
      const filename = extractFilenameFromScript(script);
      download(script, 'text/javascript', filename);
    };
    
    document.querySelector('.download-btn').addEventListener('click', downloadScript);
    .user-script {
      width: 100%;
      height: 8em;
    }
    <textarea class="user-script">// ==UserScript==
    // @name         My Awesome Script
    // @namespace    com.stackoverflow.questions
    // @version      1.0.0
    // @description  An example UserScript to download
    // @author       Mr. Polywhirl
    // @match        https://stackoverflow.com/questions/66428142
    // @grant        none
    // ==/UserScript==
    (function() {
        'use strict';
        alert('Hello World');
    })();
    </textarea>
    <button class="download-btn">Download</button>