moularization of scripts

Subscribe to moularization of scripts 10 posts, 7 voices

 
MCH Scriptwright

Hi,

I wonder, if there is a possibility to modularize scripts. Some scripts contain hundred of lines of code which makes them difficult to read and understand and also more difficult to maintain.

I am currently working on a script which gets bigger from day to day. Is there a possibility to organize userscripts in multiple files (for example one file for each definition of a class and seperate files for small library like sets of functions)? In a way that the definitions in one file will be visible to each other and that installation by the end user still can be done by one click?

 
Osias User

I believe it's possible to load the files from some url, like

If you're doing something for exclusive personal use like me, it won't matter.

 
dob Scriptwright

You can only modify the HTML by adding script code to it as strings.
But script can't read each others values set by GM_setValue() or include functions from other scripts.

But really,
what userscript needs this much code?

 
znerp Scriptwright

what userscript needs this much code?
I'm sure some scripts have a helluvalot of lines of code. My longest script is my google extra script which I believe has almost 600 lines of code, and that's a relatively simple script.

EDIT: looking at my script list again, it seems my facebook colour changer script is over 1000 lines of code, but a fair chunk of that is data URLs and some functions I used from elsewhere.

 
Osias User

>> what userscript needs this much code?

a lot. BUT even short scripts would benefit from js libraries.

 
crazysnailboy Scriptwright

Depending on how you want to use the included library, you might find this useful:
http://jimbojw.com/wiki/index.php?title=Using_P...

 
crazysnailboy Scriptwright

Here's a horrible nasty hack of a solution!

Using GM_xmlhttpRequest it's possible to load the contents of an external javascript file and evaluate the resulting xml response as a javascript string. This doesn't work with a file:// URL, so you'll need a web server. I used POW (https://addons.mozilla.org/en-US/firefox/addon/...), firefox addon (i love the idea of having a server in a browser - it's a crazy old world!!).

Ideally the GM_xmlhttpRequest call would be executed synchronously, but I'm not sure if that's possible. Consequently I've used a polling function to wait until all requested have been submitted and processed. Another issue is not being able to process the request in global scope, so the script code is stored in an array for evaluation within the scope you want to use it. That means you have to evaluate it every time you want it, which is a pain.

In a way, this doesn't really help because the code's 150 lines long. You could condense it down and strip out the white space, and you only need to include one line in each userscript i suppose.

There's got to be a more elegant way of doing this. Anybody?

var ScriptLibraries = {

	scriptURLs: [],
	scriptData: [],

	onLoad: function(){},

// _submitXmlRequests
	_submitXmlRequests: function()
	{
	for (var i = 0 ; i < ScriptLibraries.scriptURLs.length ; i++ )
		{
		GM_xmlhttpRequest
			({
			url: ScriptLibraries.scriptURLs[i],
			onload: ScriptLibraries._processXmlResponse,
			onerror: function(xmlhttpResponse) { ScriptLibraries.scriptData.push(	new function() { this.src = ""; this.code = "error" + xmlhttpResponse.status + ": " + xmlhttpResponse.statusText; return this; } ); },
			method:"GET"
			});
		}
	},


// _processXmlResponse
	_processXmlResponse: function(xmlhttpResponse)
	{
	var s = xmlhttpResponse.responseText;
	if (s.substring(0, 55) == "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">")
		{
		s = "error" + xmlhttpResponse.status + ": " + xmlhttpResponse.statusText;
		}
	ScriptLibraries.scriptData.push( new function() { this.src = ""; this.code = s; return this; } );
	},


// _waitUntilLoaded
	_waitUntilLoaded: function()
	{
	if ((this.scriptURLs.length == this.scriptData.length) && (this.scriptData[this.scriptData.length-1].code != ""))
		{
		this._whenFinished();
		}
	else
		{
		window.setTimeout( function() { ScriptLibraries._waitUntilLoaded(); } , 250, false);
		}
	},


// _whenFinished
	_whenFinished: function()
	{
	for (var i = 0 ; i < ScriptLibraries.scriptData.length ; i++ )
		{
		var sData = this.scriptData[i];
		var sURL = this.scriptURLs[i];
		sData.src = sURL.substring(( (sURL.indexOf("/") >= 0) ? sURL.lastIndexOf("/")+1 : 0 ), sURL.length-3);
		if (sData.code.substring(0, 5) == "error")
			{
			sData.code = "alert(\"" +
			"Error loading script file \\n\\\"" + sURL + "\\\"\\n\\n" +
			sData.code.substring(5, sData.code.length) + "\\n " +
			"\");";
			}
		}
	ScriptLibraries.onLoad();
	},


// loadLibraries
	loadLibraries: function()
	{
	this._submitXmlRequests();
	this._waitUntilLoaded();
	},


// script
	script: function(sScriptFile)
	{
	for (var i = 0 ; i < this.scriptData.length ; i++ )
		{
		if (sScriptFile == this.scriptData[i].src)
			{
			return this.scriptData[i].code;
			}
		}
	return "alert(\"Script file \\\"" + sScriptFile + ".js\\\" not loaded.\");";
	}


};

// doStuff

// function which will be fired after all libraries have been loaded.
// this is where the script should start performing it's processing.

function doStuff()
{
eval(ScriptLibraries.script("string"));

var s = new String("hello world");
alert(s.left(5));
}

with (ScriptLibraries)
	{
	// build the array of libraries you wish to import
	scriptURLs = [
		"http://localhost:6670/javascript/string.js"
		];

	// assign the on complete handler to run the "doStuff" function when libraries have finished being loaded
	onLoad = doStuff;

	// load the libraries
	loadLibraries();
	}

 
Aquilax Scriptwright

Wait for GM 0.8, it adds two new headers @require and @resource

http://groups.google.com/group/greasemonkey-use...

 
MCH Scriptwright

Thanks for your help.
But the current solutions are not userfriendly. I would certainly NOT trust a script that loads additional orders from a foreign resource.
Many scripts using a library that lies on some server is a severe security risk. Just think about the possibility of that library to get modified and suddenly read out cookies or other private data. This would be a huge desaster to the people's trust in greasemonkey and userscripts.
The user needs control over what code is executed in his browser.

Inclusion of librarys the user installed himself in his browser is ok.

The most userfriendly solution seems still to put everything into a single script file (I did so, and the script I work on has now more than 800 lines...)

 
The Dot Scriptwright

I *thought* greasemonkey does something like wrap in an anonymous function, execute, and throw away?

If that was the case, this wouldnt be possible.

However, if you set your functions under a global namespace i.e. object, then you could access them in different scripts.