User Script Updates

By Richard Gibson Last update Jan 3, 2012 — Installed 426,258 times.

Script Summary: Sample code for authors to add automatic update functionality to their scripts.



Version: 0.4.3

Support

Click here to lend your support to Public Domain Software at www.pledgie.com !
This software will always be open source and free of charge, but donations keep it going! No amount is too little.

Description

This sample code checks for a newer version of itself.

How to use

Add the following code anywhere in your script, changing at least source (description URL), identifier (.user.js URL),meta (.meta.js URL), version, and date to match your script (possibly calculating them automatically).

var SCRIPT = {
	 name: "User Script Updates"
	,namespace: "http://userscripts.org/people/336"
	,source: "http://userscripts.org"      // script homepage/description URL
			+ "/scripts/show/2296"
	,identifier: "http://userscripts.org"  // script URL
			+ "/scripts/source/2296.user.js"
	,meta: "http://userscripts.org"        // metadata URL
			+ "/scripts/source/2296.meta.js"
	,version: "0.0"                        // version
	,date: "2010-03-08"                    // update date
};
var UPDATE = SCRIPT.namespace + ' ' + SCRIPT.identifier;
try {
	GM_setValue(UPDATE, 1);
	if (GM_getValue(UPDATE)) {
		UPDATE = {key: UPDATE, get: GM_getValue, set: GM_setValue};
	}
	else {
		throw {};
	}
}
catch(x){
	UPDATE = {
		 set: function(key, value){ try{ localStorage.setItem(key, value); }catch(x){} }
		,get: function(key, varDefault) {
			try {
				var stored = localStorage.getItem(key);
				if(stored===null){ return varDefault; }
				return stored;
			}
			catch(x) {
				return varDefault;
			}
		}
	};
}
UPDATE = {
	 SCRIPT: SCRIPT
	,defaults: {checkDays: 3, version: SCRIPT.version, date: SCRIPT.date, name: SCRIPT.name,
			lastCheck: typeof(GM_xmlhttpRequest)!='undefined' ? 0 : (new Date()).getTime()}
	,getValue: UPDATE.get
	,setValue: UPDATE.set
	,HttpRequest: (typeof(GM_xmlhttpRequest)!='undefined' && GM_xmlhttpRequest) || function(){}
	,ready: false
	,init: function() {
		if(this.ready){ return; }
		this.ready = true;
	 	for (var name in this.defaults) {
	 		if(name in this){ delete this.defaults[name]; }
	 		else{ this[name] = this.getValue('_UPDATE_' + name, this.defaults[name]); }
	 	}
	 	for (var p in {checkDays:0, lastCheck:0}) { delete this.defaults[p]; }
	}
	,check: function(fnOnNewer, fnIsNewer, blnForce) {
		this.init();
		var interval = Math.max(parseFloat(this.checkDays) * 24 * 60 * 60 * 1000, 0) || Infinity;
		var diff = (new Date()) - parseInt(this.lastCheck,10);
		if(!blnForce && !this.isNewer(this, this.SCRIPT, fnIsNewer) && !(diff > interval)){ return false; }
		if (blnForce || (diff > interval)) {
			var t = this;
			return this.HttpRequest({method: 'GET', url: this.SCRIPT.meta || this.SCRIPT.identifier, onload: function(r){
				t.setValue('_UPDATE_' + 'lastCheck', t.lastCheck = '' + (new Date()).getTime());
				t.parse(r.responseText, [fnOnNewer, fnIsNewer, false]);
			}});
		}
		try{ fnOnNewer(this, this.SCRIPT); }catch(x){}
	}
	,parse: function(strResponse, arrCheckArgs) {
		var re = /\/\/\s*(?:@(\S+)\s+(.*?)\s*(?:$|\n)|(==\/UserScript==))/gm, match = true, name;
		while (match && (match = re.exec(strResponse))) {
			if(match[3]){ match = null; continue; }
			name = match[1];
			if(name in this.defaults){ this.setValue('_UPDATE_' + name, this[name] = match[2]); }
		}
		this.check.apply(this, arrCheckArgs || []);
	}
	,isNewer: function(objUpdate, objScript, fnIsNewer) {
		if(!objUpdate){ objUpdate = this; }
		if(!objScript || (objUpdate.date > objScript.date)){ return true; }
		try {
			return fnIsNewer(objUpdate, objScript);
		}
		catch (x) {
			return (!(objUpdate.date < objScript.date) && (objUpdate.version != objScript.version));
		}
	}
};
Then, during initialization or whenever appropriate, check for an update with
UPDATE.check(fnOnNewer(objUpdate, objScript) [,fnIsNewer(objUpdate, objScript) [,blnForce]])
fnOnNewer(objUpdate, objScript)
Callback to execute if a newer version is found. Will be passed an update object (see below) and objScript.
fnIsNewer(objUpdate, objScript)
[OPTIONAL] Callback to execute for determining if the retrieved script is newer than the installed script. Will be passed an update object and objScript. The update object will all the same properties as objScript, with the latest values taken from the ==UserScript== metadata(@version; @date; etc.). If not specified, scripts will be treated as updates when @date is greater than objScript.date or @date is not less than objScript.date and @version does not equal objScript.version.
blnForce
[OPTIONAL] Whether or not to force an HTTP request for new data. If not specified, an HTTP request will only be made every UPDATE.checkDays days (defaulting to UPDATE.defaults.checkDays)

Changelog

  • 0.4.3 (2012-01-02)
    • Updated: Google Chrome support
  • 0.4.2 (2011-02-21)
    • Fixed: Firefox/Windows security bug
  • 0.4.1 (2011-02-19)
    • Updated: update checking with meta.js
  • 0.4 (2010-03-08)
    • New: Script reboot; 100% new code and demonstrative functionality only