9 points

@require usoCheckup Automatic Script Updater

Last update Nov 12, 2009

What is it?

Having a hard time distributing a script to users because not everyone comes back for an update? Well here's another solution that offers automatic script updating via the Greasemonkey @require usoCheckup Automatic Script Updater.

top | bottom

How is it used?

userscripts.org URL
***** This URL is still pending RoR transcode and will not work directly yet. *****
// @require    http://userscripts.org/usocheckup/scriptid.js
***** This URL is still pending RoR transcode and will not work directly yet. *****

Jesse is currently considering hosting this directly on userscripts.org and it is anxiously awaited... however in the meantime the following mirror(s) is available RIGHT NOW!
top | bottom

Approved Mirrors

Insert this @require key into your metadata block with the designated protocol, host, pathname and scriptid.

Server URL
//@require http://usocheckup.dune.net/13701.js
(TIP: Don't forget to change the scriptid value to the new number of the script to be automatically updated.)
THAT'S IT! :)
To see how it works and fully customize usoCheckup please continue reading below...
top | bottom

Contributors

Jesse AndrewsPhotodeussizzlemctwizzlelucideerTim Smart
Johan SundströmJoeSimmonsMarti MartzBuzzyIzzySoft

top | bottom

Description

The @require usoCheckup Automatic Script Updater is a "behind the scenes" script update mechanism. It has been designed to keep the footprint as compact as possible while maintaining maximum flexibility and of course minimal impact on the actual script source.

Throughout this guide there will be references to additional String Query Parameters (SQP) and JavaScript API (API) methods available to this Automatic Script Updater along with a host full of examples and linked material.
Current Compatibility Matrix
icon
Mozilla Firefox
Mozilla Seamonkey
KDE Konqueror
Apple Safari
Opera Software Opera
Microsoft Internet Explorer
Google Chrome
Linux
Greasemonkey 0.5b+
Greasemonkey Port
-
-
-
-
-
Macintosh
Greasemonkey 0.5b+
Greasemonkey Port
-
-
-
-
-
Windows
Greasemonkey 0.5b+
Greasemonkey Port
-
-
-
-
-
(TIP: Hover the mouse over an icon)
top | bottom

Syntax

// @require protocol//host/pathname/scriptid.js?maxage=30&method=show&open=GM&id=()&custom=0&lang=en&trim=lang&xhr=GM&storage=GM
Value: URI

top | bottom

URL String Query Parameters

scriptid
Value: Number
Usage: protocol//host/pathname/scriptid.js
Default: undefined
top | bottom

maxage
Value: Number
Usage: maxage=[ 1... ]
Default: 30
  • Maximum age or interval in days before next automatic update check.
  • This updater has a interesting update check pattern. Instead of starting off checking for updates at a consistent interval, it uses a special algorithm called an exponential backoff to help assist scriptwrights deliver their scripts.

    The frequency of update checking is approximately 1 hour, 3 hours, 9 hours, 24 hours, 3 days, 7 days, 21 days ... until the maximum age is reached. At this point automatic checking will return to a linear interval and only check at the maximum age until it encounters an update where it restarts the entire cycle all over.
top | bottom

method
Value: String
Usage: method=[ show | install | update ]
Default: show
  • Changes how the updater executes.
  • When an update is found...
  • show
    ...will open the homepage of the script on userscripts.org.
    alert widget - updateAvailable showHello, World Homepage Screenshot
    install
    ... will trigger the Greasemonkey installation dialog.
    Some platforms MAY NOT ALWAYS WORK with this method due to an intermittent bug in Greasemonkey. The TEMPORARY WORKAROUND is, instead of clicking the Greasemonkey install button in the banner... click to the browser address bar then press enter to trigger the installation dialog.
    alert widget - updateAvailable installGreasemonkey Install Dialog
    update
    ...will trigger the Greasemonkey installation dialog. but keeps the userscripts.org install counter from incrementing. This method currently uses a unique "feature" in string query parameters on userscripts.org to achieve this goal.
    Some platforms MAY NOT ALWAYS WORK with this method due to an intermittent bug in Greasemonkey. The TEMPORARY WORKAROUND is, instead of clicking the Greasemonkey install button in the banner... click to the browser address bar then press enter to trigger the installation dialog.
top | bottom

open
Value: String
Usage: open=[ GM | window ]
Default: GM
  • Changes how the updater opens the method.
  • When an update is found, and a user accepts the query confirmation, it...
  • GM
    ...will open the default method via GM_openInTab.
    window
    ...will open the default method in the current window.
    Some pop-up blockers may interfere with this opening method.
top | bottom

id
Value: String
Usage: id=[ usoCheckup_13701 | USO.checkup_13701 | ... ]
(TIP: Don't forget to change the scriptid value to the new number of the script to be automatically updated.)
Default: (Anonymous Function)
  • It is not recommended to set this unless you are an experienced ScriptWright.
  • usoCheckup currently uses an anonymous function, however if a user would like to set a different name and expose the object for full or partial customization this SQP needs to be set. Please be aware that some browsers don't have the luxury of the Sandbox that Greasemonkey currently provides; so choose the identifier name wisely and uniquely.
  • Current allowable character combinations are A-Z, a-z, 0-9, _ (underscore), and a single . (period) for object creation. If invalid character combinations are used the updater will automatically revert back to an anonymous function.
    Please remember not to use any reserved DOM or JavaScript identifiers (variable names), including JavaScript 1.7 and JavaScript 1.8 reserved words, or the entire script may break.
  • See Core Example Method C for a common usage while using the Greasemonkey Sandbox.
top | bottom

custom
Value: Boolean
Usage: custom=[[ 0 | false | no ] | [ 1 | true | yes ]]
Default: 0 | false | no
  • Use this to exclude the default widget handlers when defining custom widget handlers.
  • See Core Example Method D.
top | bottom

lang
Value: String
Usage: lang=[ en | ... ]
Default: en
  • usoCheckup can speak a different language! Usually this is determined automatically but in the rare circumstances forcing it to a different language is possible.
  • It is not recommended to set this, as it will override the automatic browser language detection.
  • Use the two digit ISO 639-1 Code for alternate language support. Additional unreferenced codes can be found at Wikipedia.
    alert widget - updateAvailableGerman alert widget - updateAvailableItalian alert widget - updateAvailableJapanese alert widget - updateAvailable
    (and many more)
top | bottom

trim
Value: String
Usage: trim=[ en [, de [, ... ] ] ]
Default: (None)
  • Use this to exclude the default language translation for a specific set of locales and define a custom set of strings.
  • See JavaScript API strings and Core Example Method E.
top | bottom

xhr
Value: String
Usage: xhr=[ GM ]
Default: GM
  • Changes how the updater retrieves the script meta.js routine.
  • GM
    ...will retrieve the meta.js via GM_xmlhttpRequest.
More request method types may eventually become available.
top | bottom

storage
Value: String
Usage: storage=[ GM ]
Default: GM
  • Changes how the updater stores preferences.
  • GM
    ...will store the updater preferences via GM_setValue.
More storage types may eventually become available.
top | bottom

JavaScript API

The JavaScript API is only available when the id SQP is specified.
usoCheckup is assumed to be the specified id for all examples in this guide.
enabled
Value: Boolean
Usage:
usoCheckup.enabled=[ true | false ];
var value = usoCheckup.enabled;
Default: true
Returns: Boolean
  • Toggles enabling or disabling of the automatic updater.
top | bottom

maxage
Value: Number
Usage:
usoCheckup.maxage=[ 1... ];
var value = usoCheckup.maxage;
Default: 30
Returns: Number
  • Maximum age or interval to check measured in days.
top | bottom

updateUrl
Value: JSON Object
Usage: var method = usoCheckup.updateUrl["element"];
Returns: String
  • Defines a list of update paths. These strings are dynamically generated.
  • Available elements:
  • default
    "show" | "install"
    install
    "https://userscripts.org/scripts/source/scriptid.user.js"
    show
    "http://userscripts.org/scripts/show/scriptid/"
top | bottom

openUrl
Value: Function
Usage: usoCheckup.openUrl(url);
  • Opens a url programatically.
top | bottom

strings
Value: Function
Usage:
var someString = usoCheckup.strings("element");
var someNewString = usoCheckup.strings("newelement", "Some new string");
Returns: String
  • Defines a list of common strings. These strings are dynamically generated and will be localized into the default browser language.
  • If additional strings are added they immediately become read only after adding. It is also strongly recommended that the proper translation occur before committing new strings. See Core Example Method E for a staticly defined locale.
  • Existing predefined strings can not be changed unless trim is specified in the String Query Parameters. Users must include the complete set of predefined string elements if using the default widgets or the entire script will break. See Core Example Method E for proper usage.
  • Available elements:
    ({
    "lang":              "en",
    "updateAvailable":   "An update is available.",
    "updateUnavailable": "No update available.",
    "updateMismatched":  "WARNING: Metadata does not match!",
    "updateUnlisted":    "WARNING: Script is not listed!",
    "queryWidget":       "Check for an update.",
    "toggleWidget":      "Toggle automatic update.",
    "updaterOff":        "Automatic update is disabled.",
    "updaterOn":         "Automatic update is enabled.",
    "showConfirm":       "Show the script homepage?",
    "installConfirm":    "Install the script?",
    "closeMessage":      "Close this message?",
    "closeAllMessages":  "Close all messages?"
    })
    Strings generated in Russian locale browser language:
    ({
    "lang":              "ru",
    "updateAvailable":   "Обновление доступно.",
    "updateUnavailable": "Нет доступных обновлений.",
    "updateMismatched":  "ПРЕДУПРЕЖДЕНИЕ: Metadata не совпадают!",
    "updateUnlisted":    "ПРЕДУПРЕЖДЕНИЕ: Сценарий нет в списке!",
    "queryWidget":       "Проверить обновления.",
    "toggleWidget":      "Переключение автоматическое обновление.",
    "updaterOff":        "Автоматические обновления Off.",
    "updaterOn":         "Автоматические обновления включены.",
    "showConfirm":       "Вы хотите, чтобы показать начало сценария?",
    "installConfirm":    "Вы хотите, чтобы показать начало сценария?"
    "closeMessage":      "Закрыть это сообщение?",
    "closeAllMessages":  "Закройте все сообщения?"
    })
    (and so on... One language per usoCheckup Automatic Script Updater.)
top | bottom

updaterMeta
Value: JSON Object
Usage: var value = usoCheckup.updaterMeta["element"];
Returns: String
  • Returns a string for the specified JSON index from the updater metadata.
top | bottom

localMeta
Value: JSON Object
Usage: var value = usoCheckup.localMeta["element"];
Returns: String
  • Returns a string for the specified JSON index from the last stored userscripts.org retrieved metadata.
top | bottom

parseMeta
Value: Function
Usage: var jsonMeta = usoCheckup.parseMeta(metadataBlock);
Returns: JSON Object
  • Parses a raw metadata block string into a JSON encoded object.
// ==UserScript==
// @name          Hello, World
// @namespace     http://localhost
// @description   JavaScript alert box saying Hello, world
// @copyright     2007+, Marti Martz (http://userscripts.org/users/37004)
// @license       GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @license       (CC); http://creativecommons.org/licenses/by-nc-sa/3.0/
// @version       0.0.1
// @include       http://www.example.com/*
// @include       http://www.example.net/*
// @include       http://www.example.org/*
// @require       http://usocheckup.dune.net/13701.js?method=install
// @uso:unlisted
// ==/UserScript==






({
"name": "Hello, World",
"namespace": "http://localhost",
"description": "JavaScript alert box saying Hello, world",
"copyright": "2007+, Marti Martz (http://userscripts.org/users/37004)",
"license": [
  "GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html",
  "(CC); http://creativecommons.org/licenses/by-nc-sa/3.0/"
],
"version": "0.0.1",
"include": [
  "http://www.example.com/*",
  "http://www.example.net/*",
  "http://www.example.org/*"
],
"require": "http://usocheckup.dune.net/13701.js?method=install",
"uso": {
  "unlisted": ""
}
})
(TIP: Use browser zoom to view raw versus JSON.)
top | bottom

userMeta
Value: JSON Object
Usage: var value = usoCheckup.userMeta["element"];
Returns: String
  • This dynamic object is created by the end user using E4X XMLList encapsulation around the files metadata block. A scriptwright MUST CURRENTLY USE E4X to achieve this.
  • Returns a string for the specified JSON index from the local script copy metadata.
  • See Core Example Method B.
top | bottom

request
Value: Function
Usage: usoCheckup.request([ true | false ]);
Default: false
  • Programattically checks for an update.
  • All requests are limited to once every 15 minutes per page session. If checked more than once within that time period, no visible notification will be given.
  • If an update is found then the alert widget will be activated.
  • Setting the parameter to true will enable displaying of the updateUnavailable alert box and will also not increment the automatic checkup counter.
    alert widget - updateUnavailable
top | bottom

widgets
Value: Function
General Usage:
usoCheckup.widgets("element");
usoCheckup.widgets("element", function() { /* some code */ });
  • Defines and/or programatically triggers a widget.
  • Core widget elements:
    alert
    Value: Function
    Usage: usoCheckup.widgets("alert"), function(details) { /* some code */ });
    Default: Simple modal dialog confirmation box or alert box.
    • Assigns a custom callback handler when an automatic update is available.
    • See Core Example Method D.
      alert widget - updateAvailablealert widget - updateUnavailable
    forced
    Value: Boolean
    Usage: var isForced = details.forced;
    Returns: Boolean
    • This value is set to true if the request function was activated with a true parameter.
    mismatched
    Value: Boolean
    Usage: var isMismatched = details.mismatched;
    Returns: Boolean
    • This value is set to true if the script name or namespace have changed since last install. When this happens automatic updating will be disabled.
    unlisted
    Value: Boolean
    Usage: var isUnlisted = details.unlisted;
    Returns: Boolean
    • This value is set to true if the scriptwright has chosen to self unlist this script.
    • Any instance of the updater that encounters a self unlisted script will change to method show if set to any other value. This is critical to avoid error messages and bring the attention to the user that unlisted status has occurred.
    • Unlisted scripts are determined by the source authors inclusion of the the phantom uso metadatablock imperative of @uso:unlisted.
    • See Core Example Method F.
    remoteMeta
    Value: JSON Object
    Usage: var value = details.remoteMeta["element"];
    Returns: String
    • Returns a string for the specified JSON index from the the dynamically userscripts.org retrieved metadata.
query
Value: Function
Usage:
usoCheckup.widgets("query");
usoCheckup.widgets("query"), function() { /* some code */ });
Default: Widget is a GM_registerMenuCommand which calls the request function with true parameter.
  • Optionally assigns a custom callback handler.
  • See Core Example Method D.
    query widget queryWidget
toggle
Value: Function
Usage:
usoCheckup.widgets("toggle");
usoCheckup.widgets("toggle"), function() { /* some code */ });
Default: Widget is a GM_registerMenuCommand. This will display a modal dialog alert box.
  • Optionally assigns a custom callback handler.
  • See Core Example Method D.
    toggle widget updaterOntoggle widget updaterOfftoggle widget updaterOn/Off
top | bottom

Examples

Core

Method A
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/gmscripts
// @description   Scripting is fun
// @copyright     2009+, John Doe (http://www.example.com/~jdoe)
// @license       GPL v3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @license       (CC); http://creativecommons.org/licenses/by-nc-sa/3.0
// @version       1.0.0
// @include   http://www.example.com/*
// @require   http://pathto/scriptid.js
// ==/UserScript==
top | bottom

Method B
if (typeof usoCheckup != "undefined")
  usoCheckup.userMeta = usoCheckup.parseMeta(<><![CDATA[
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/gmscripts
// @description   Scripting is fun
// @copyright     2009+, John Doe (http://www.example.com/~jdoe)
// @license       GPL v3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @license       (CC); http://creativecommons.org/licenses/by-nc-sa/3.0
// @version       1.0.1
// @include   http://www.example.com/*
// @require   http://pathto/scriptid.js?id=usoCheckup
// ==/UserScript==
]]></>);
top | bottom

Method C
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/gmscripts
// @description   Scripting is fun
// @copyright     2009+, John Doe (http://www.example.com/~jdoe)
// @license       GPL v3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @license       (CC); http://creativecommons.org/licenses/by-nc-sa/3.0
// @version       1.0.2
// @include   http://www.example.com/*
// @require   http://pathto/scriptid.js?maxage=7&method=install&id=usoCheckup
// ==/UserScript==

  /*
      NOTE: Please use these sparingly.  If everyone uses these,
        the Monkey Menu may become cluttered and lose usefulness.
  */

  if (typeof usoCheckup != "undefined") {
    usoCheckup.widgets("query");      // Activate the default query widget
    usoCheckup.widgets("toggle");     // Activate the default toggle widget
  }
top | bottom

Method D
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/gmscripts
// @description   Scripting is fun
// @copyright     2009+, John Doe (http://www.example.com/~jdoe)
// @license       GPL v3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @license       (CC); http://creativecommons.org/licenses/by-nc-sa/3.0
// @version       1.0.3
// @include   http://www.example.com/*
// @require   http://pathto/scriptid.js?maxage=7&method=show&custom=yes&id=usoCheckup
// ==/UserScript==

  if (typeof usoCheckup != "undefined") {
    /*
        Define a custom alert widget
    */

    usoCheckup.widgets("alert", function(details) {
      if (parseInt(details.remoteMeta["uso"]["version"])
        > parseInt(usoCheckup.localMeta["uso"]["version"])) {
        if (confirm([
          usoCheckup.localMeta["name"],
          "",
          usoCheckup.strings("updateAvailable"),
          ((usoCheckup.updateUrl["default"] === "install")
            && !details.mismatched && !details.unlisted)
            ? usoCheckup.strings("installConfirm")
            : usoCheckup.strings("showConfirm")
        ].join("\n"))) {
          if (details.mismatched || details.unlisted)
            usoCheckup.openUrl(usoCheckup.updateUrl["show"]);
          else
            usoCheckup.openUrl(usoCheckup.updateUrl[usoCheckup.updateUrl["default"]]);
          }
      }
      else if (details.forced)
        alert([
          usoCheckup.localMeta["name"],
          "",
          usoCheckup.strings("updateUnavailable")
        ].join("\n"));
    });

    /*
        Define a custom query widget
    */

    usoCheckup.widgets("query", function() {
      GM_registerMenuCommand(
        usoCheckup.localMeta["name"] + ": " + usoCheckup.strings("queryWidget"),
        function() {
          usoCheckup.request(true);
        }
      );
    });
    usoCheckup.widgets("query");      // Activate the custom query widget

    /*
        Define a custom toggle widget
    */

    usoCheckup.widgets("toggle", function() {
      GM_registerMenuCommand(
        usoCheckup.localMeta["name"] + ": " + usoCheckup.strings("toggleWidget"),
        function() {
          if (usoCheckup.enabled === true) {
            usoCheckup.enabled = false;
            alert([
              usoCheckup.localMeta["name"],
              "",
              usoCheckup.strings("updaterOff")
            ].join("\n"));
          }
          else {
            usoCheckup.enabled = true
            alert([
              usoCheckup.localMeta["name"],
              "",
              usoCheckup.strings("updaterOn")
            ].join("\n"));
          }
        }
      );
    });
    usoCheckup.widgets("toggle");     // Activate the custom toggle widget
  }
top | bottom

Method E
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/gmscripts
// @description   Scripting is fun
// @copyright     2009+, John Doe (http://www.example.com/~jdoe)
// @license       GPL v3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @license       (CC); http://creativecommons.org/licenses/by-nc-sa/3.0
// @version       1.0.4
// @include   http://www.example.com/*
// @require   http://pathto/scriptid.js?trim=de&id=usoCheckup
// ==/UserScript==

  /*
      NOTE: Please use this sparingly and appropriately.
  */

  if (typeof usoCheckup != "undefined") {
    switch (usoCheckup.strings("lang")) {
      case "de":  // Strings contributed by user Basique.
        with (usoCheckup) {
          strings("updateAvailable", "Ein Update ist verfügbar.");
          strings("updateUnavailable", "Kein Update verfügbar.");
          strings("updateMismatched", "WARNUNG: Metadaten stimmen nicht überein!");
          strings("updateUnlisted", "WARNUNG: Nicht aufgelistetes Skript!");
          strings("queryWidget", "Prüfe auf Updates.");
          strings("toggleWidget", "Umschalten der automatischen Updates.");
          strings("updaterOff", "Automatische Updates sind ausgeschaltet.");
          strings("updaterOn", "Automatische Updates sind eingeschaltet.");
          strings("showConfirm", "Wollen Sie die Homepage des Skripts öffnen?");
          strings("installConfirm", "Wollen Sie das Skript zu installieren?");
          strings("closeMessage", "Schließen Sie diese Nachricht?");
          strings("closeAllMessages", "Schließen Sie alle Einträge?");
        }
        break;
    }

    usoCheckup.widgets("query");   // Activate the default query widget.
    usoCheckup.widgets("toggle");  // Activate the default toggle widget.
  }
top | bottom

Method F
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/gmscripts
// @description   Scripting is fun
// @copyright     2009+, John Doe (http://www.example.com/~jdoe)
// @license       GPL v3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @license       (CC); http://creativecommons.org/licenses/by-nc-sa/3.0
// @version       1.0.5
// @include   http://www.example.com/*
// @require   http://pathto/scriptid.js
// @uso:unlisted
// ==/UserScript==
top | bottom

More

***** These examples are experimental and may be subject to change *****
Method 1
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/gmscripts
// @description   Scripting is fun
// @copyright     2009+, John Doe (http://www.example.com/~jdoe)
// @license       GPL v3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @license       (CC); http://creativecommons.org/licenses/by-nc-sa/3.0
// @version       0.0.1
// @include   http://www.example.com/*
// @require   http://pathto/scriptid.js?maxage=14&method=update
// ==/UserScript==
top | bottom

See Also

userscripts.org Discussion Group
Primary Code Homepage
Secondary Code Homepage
http://userscripts.org/topics/29195
http://userscripts.uservoice.com/pages/general/suggestions/144246

top | bottom

Notes

Many thanks to all those Code Monkeys who contributed concepts, code, time, energy and MAKING SOME NOISE! :)

top | bottom

Other Stand-Alone Updaters

Each of these scripts also do updating with different techniques and perhaps some specific refinements, limitations or possibility of being broken.
This is by no means an exhaustive list.
top | bottom