@require usoCheckup Automatic Script Updater

Written by Marti — Last update Dec 7, 2011

20 points
NOTE: SOME LINKS ON THIS PAGE WILL NOT WORK WITHOUT uso - Anchor Bookmarks
Beginner

What is it?

How is it used?

Approved Mirrors

Contributors

Advanced

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 for ScriptWrights. If you are a User wanting to possibly add update checking to an existing script please try out uso - installWith


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!

Approved Mirrors

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

Server URL
// @require http://usocheckup.redirectme.net/13701.js
(TIP: Don't forget to change the scriptid value to the new number of the script to be automatically updated.)
#####################################
#####################################
#####################################
#####################################
#####################################
#####################################
#####################################

#############
#############
#############
#############
#############
#############
#############

##################
##################
##################
##################
##################
##################
##################

To see how it works and fully customize usoCheckup please continue reading below...

Contributors

Jesse Andrewsn5zhkylnJoeSimmonsMarti MartzBuzzyNVBasique
Johan SundströmPhotodeussizzlemctwizzlelucideerTim SmartIzzySoft
Many thanks to all those Code Monkeys who contributed concepts, code, time, energy and MAKING SOME NOISE!

Advanced

Query String Parameters

JavaScript API

Examples

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 Query String Parameters(QSP) and JavaScript API(API) methods available to this Automatic Script Updater along with a host full of examples and linked material.
Compatibility Matrix
icon
Mozilla Firefox
Mozilla Seamonkey
KDE Konqueror
Apple Safari
Opera Software Opera
Microsoft Internet Explorer
SourceForge ChromiumGoogle Chrome (Intel Only)
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)


Query String Parameters

// @require http://pathto/scriptid.js?wrapperid=scriptid&maxage=30&minage=1&method=show&open=GM&id=()&custom=0&topicid=topicid&lang=en&trim=lang
Value: URI

scriptid
Value: Number
Usage: http://pathto/scriptid.js
Default: undefined


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.


minage
Value: Number
Usage: minage=[ 1...(maxage * 24)]
Default: 1
  • Minimum age or delay in hours before first automatic update check.
  • This value is also designed to skew or stretch the exponential backoff algorithm to decrease the frequency of updates.
  • Default start intervals are approximately 1h, 3h, 9h, 1d, 3d, 7d, 21d, and so on until maxage is reached.
    Setting a minage to 72 hours (3d * 24h = 72h) will produce this approximate start pattern of checking: 3d, 7d, 28d, and so on until maxage is reached.
  • If minage is equal to maxage times 24 then exponential backoff will be disabled and a normal linear interval is used always. This is usually not recommended to do.
  • minage must always be less then maxage times twenty-four. Invalid values entered will revert to the above default.


method
Value: String
Usage: method=[ show | install | update ]
Default: show


topicid
Value: Number
Usage: topicid= [ 0... ]
Default: undefined
  • When using the usoCheckup - DOMNotify Theme this sets the topic number that is opened when a user clicks on the notes icon. Typically this is used for a changelog topic under the Discussions tab of a script.
  • If the value is set to 0 (zero) then it will open up the scripts Discussions page. Please note when using wrapperid it will open the host script Discussions page. If this is undesired please set it to a topicid in the wrapper or omit this QSP.
  • See Core Example Method G


wrapperid
Value: Number
Usage: wrapperid= [ 1... ]
Default: undefined
  • It is not recommended to set this unless you are an experienced ScriptWright.
  • Changes how the updater executes.
  • When an update is found opens this uso scriptid instead of the default uso scriptid.


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.


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 QSP 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.


custom
Value: Boolean
Usage: custom=[[ 0 | false | no ] | [ 1 | true | yes ]]
Default: 0 | false | no


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 in debug mode.
  • 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)


trim
Value: String
Usage: trim=[ en [, de [, ... ] ] ]
Default: (None)


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.


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.


debug
Value: Boolean
Usage: debug=[[ 0 | false | no ] | [ 1 | true | yes ]]
Default: 0 | false | no
  • Use this to output debug messages to the error console.


JavaScript API

The JavaScript API is only available when the id QSP is specified.
usoCheckup is assumed to be the specified id for all examples in this guide.

enabled

updaterMeta

updateUrl

strings

maxage

localMeta

openUrl

request

minage

parseMeta

widgets

userMeta

enabled
Value: Boolean
Usage:
usoCheckup.enabled=[ true | false ];
var value = usoCheckup.enabled;
Default: true
Returns: Boolean
  • Toggles enabling or disabling of the automatic updater.


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


minage
Value: Number
Usage:
usoCheckup.minage=[ 1...(maxage * 24) ];
var value = usoCheckup.minage;
Default: 1
Returns: Number
  • Minimum age or delay to check measured in hours.


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/"


openUrl
Value: Function
Usage: usoCheckup.openUrl(url [, default ]);
  • Opens a url programatically.
  • If a boolean true value is present in the second parameter then it will force that url to open with GM_openInTab regardless of the requested open method..


strings
Value: Function
Usage:
var someString = usoCheckup.strings("element");
var someNewString = usoCheckup.strings("newelement", "Some new string");
var someLastString = usoCheckup.strings({"newelement1": "Some new string1", "newelement2": "Some new lastString2"});
var someLastString = usoCheckup.strings();
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 Query String Parameters. Users must either include the complete set of predefined string elements if using the default widgets or execute usoCheckup.strings() in the top-level script 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?",
    "topicConfirm":      "View the script topic?",
    "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":    "Вы хотите, чтобы показать начало сценария?"
    "topicConfirm":      "Открыть сценария теме?",
    "closeMessage":      "Закрыть это сообщение?",
    "closeAllMessages":  "Закройте все сообщения?"
    })
    (and so on... One language per usoCheckup Automatic Script Updater.)


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


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.


parseMeta
Value: Function
Usage: var jsonMeta = usoCheckup.parseMeta(metadataBlock);
Returns: JSON Object
  • Parses a raw metadata block string into a JSON encoded object.
INPUT:
// ==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 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/*
// @include       http://www.example.net/*
// @include       http://www.example.org/*
// @require       http://pathto/13701.js?method=install
// @uso:unlisted
// ==/UserScript==
OUTPUT:
({
"name": "Hello, World",
"namespace": "http://localhost",
"description": "JavaScript alert box saying Hello, world",
"copyright": "2007+, Marti Martz (http://userscripts.org/users/37004)",
"license": [
  "GPL v3 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://pathto/13701.js?method=install",
"uso": {
  "unlisted": ""
}
})


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.


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


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.
    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.
    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.


Examples

Core

More

Core

Method A

Method B

Method C

Method D

Method E

Method F

Method G


Method A
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/userscripts
// @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==


Method B
if (typeof usoCheckup != "undefined")
  usoCheckup.userMeta = usoCheckup.parseMeta(<><![CDATA[
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/userscripts
// @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==
]]></>);


Method C
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/userscripts
// @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
  }


Method D
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/userscripts
// @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
  }


Method E
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/userscripts
// @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.
        if (navigator.language == "de-DE") {
          usoCheckup.strings({
            "updateAvailable": 'Ein Update ist verfügbar.',
            "updateUnavailable": 'Kein Update verfügbar.',
            "updateMismatched": 'WARNUNG: Metadaten stimmen nicht überein!',
            "updateUnlisted": 'WARNUNG: Nicht aufgelistetes Skript!',
            "queryWidget": 'Prüfe auf Updates.',
            "toggleWidget": 'Umschalten der automatischen Updates.',
            "updaterOff": 'Automatische Updates sind ausgeschaltet.',
            "updaterOn": 'Automatische Updates sind eingeschaltet.',
            "showConfirm": 'Wollen Sie die Homepage des Skripts öffnen?',
            "installConfirm": 'Wollen Sie das Skript zu installieren?',
            "topicConfirm": 'Wollen Sie das Diskussionsthema des Skripts anzeigen?',
            "closeMessage": 'Diese Nachricht schließen?',
            "closeAllMessages": 'Alle Nachrichten schließen?'
          });
        }
        break;
    }
    usoCheckup.strings();          // Flush any missed trimmed strings that weren't defined.
  }


Method F
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/userscripts
// @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==


Method G
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/userscripts
// @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.6
// @include   http://www.example.com/*
// @require   http://pathto/scriptid.js?custom=yes&id=usoCheckup&topicid=topicid
// @require   http://userscripts.org/scripts/source/61794.user.js
// ==/UserScript==

  /*
    TIP: Don't forget to set your userscripts.org topicid
         (Example: 23565 is the topicid for Hello, World)
  */


More

***** These examples are highly experimental and may be subject to change *****

Method 1


Method 1
// ==UserScript==
// @name          My Script
// @namespace     http://www.example.com/userscripts
// @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==




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.