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!
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.)
To see how it works and fully customize usoCheckup please continue reading below...
Contributors










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. |
|
Syntax
// @require protocol//host/pathname/scriptid.js?maxage=30&method=show&open=GM&id=()&custom=0&lang=en&trim=lang&xhr=GM&storage=GMValue: URI
top | bottom
URL String Query Parameters
scriptid-
Value: Number
Usage:protocol//host/pathname/scriptid.js
Default: undefined- Set this to your current numeric script id hosted on userscripts.org.
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.
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.

- install
- ... will trigger the Greasemonkey installation 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.
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.
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.)
- 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.
- 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- Use this to exclude the default widget handlers when defining custom widget handlers.
- See Core Example Method D.
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.

(and many more)
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.
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.
storage
-
Value: String
Usage:storage=[ GM ]
Default: GM- Changes how the updater stores preferences.
- GM
- ...will store the updater preferences via GM_setValue.
JavaScript API
enabled-
Value: Boolean
Usage:usoCheckup.enabled=[ true | false ];var value = usoCheckup.enabled;
Returns: Boolean- Toggles enabling or disabling of the automatic updater.
maxage
-
Value: Number
Usage:usoCheckup.maxage=[ 1... ];var value = usoCheckup.maxage;
Returns: Number- Maximum age or interval to check measured in days.
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);- Opens a url programatically.
strings
-
Value: Function
Usage:var someString = usoCheckup.strings("element");var someNewString = usoCheckup.strings("newelement", "Some new 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.)
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.
// ==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.)
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
truewill enable displaying of the updateUnavailable alert box and will also not increment the automatic checkup counter.
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
trueif the request function was activated with atrueparameter.
- This value is set to
- mismatched
- Value: Boolean
- Usage:
var isMismatched = details.mismatched;- Returns: Boolean
- This value is set to
trueif 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
trueif 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.
- This value is set to
- 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.
usoCheckup.widgets("query");usoCheckup.widgets("query"), function() { /* some code */ });
true parameter.usoCheckup.widgets("toggle");usoCheckup.widgets("toggle"), function() { /* some code */ });
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 GroupPrimary 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

















login to vote
This is much needed and is ready to roll! :)
For Q&A please join the Discussion Group!
login to vote
best thing since sliced bread
login to vote
Out of curiosity, what was the security flaw?
login to vote
The usage of...
(function() { someObject.properties... ... window.someObject.properties... } )();... exposes a hole when scaled out as far as I've taken it.Unfortunately your methodology doesn't fully work cross-platform, and maybe even intra-browser as well, when the updater is styled as above on two or more scripts. Works fine for a single script but I'd rather not have the potential for the next script in the chain messing around with any of the others in the same namespace.
I've removed this methodology from usoCheckup and it's back in business! :)
login to vote
nice addition of pictures to see what is going on
login to vote
Method B is not a good method if you use Opera...
login to vote
login to vote
Well... i have installed Opera just to see the problems with my big script. And one of them is the xml tag. (others are the substr(str,i,l), scrollX/scrollY, the 'change' event, the Function.name, the lack of trick to put a div over an embed flash, ...)
login to vote
It is my understanding that there is currently no way of simulating some of these. I left the possibility open in usoCheckup to be able to accommodate Opera when, and if, they do catch up. The same goes for IE, Safari, Konqueror and Chrome. All of those browsers currently lack the scalability that GM has had for a while with the Greasemonkey API.
I'm glad that usoCheckup doesn't use any of your currently mentioned, Opera incompatible, standard JavaScript methods. ;) Thanks for the heads up. :)
The good thing about this updater, is if Greasemonkey isn't present, it will "soft exit"... so the scriptwright script should work under any browser if the author designed it that way. :)
login to vote
Seems like russian text written by robot :-)
({ "lang": "ru", "updateAvailable": "Обновление доступно.", "updateUnavailable": "Нет доступных обновлений.", "updateMismatched": "ПРЕДУПРЕЖДЕНИЕ: Metadata не совпадают!", "updateUnlisted": "ПРЕДУПРЕЖДЕНИЕ: Сценарий нет в списке!", "queryWidget": "Проверить обновления.", "toggleWidget": "Переключение автоматическое обновление.", "updaterOff": "Автоматические обновления Off.", "updaterOn": "Автоматические обновления включены.", "showConfirm": "Вы хотите, чтобы показать начало сценария?", "installConfirm": "Вы хотите, чтобы показать начало сценария?" })login to vote