Goon is a JavaScript tool for DOM-based on-demand script loading.

Version 1.1 – 2007-02
Copyright (c) Andrea Ercolino
Published under the MIT License

Features

  • Independent | Goon is a selfcontained script, no external library needed
  • Short | Goon is less than 2 KB (packed)
  • Rigorous | Goon guarantees that required scripts are executed before the requiring script is executed
  • Clean | Goon is tree oriented. Each gooned script is a node: if it’s not required by another node, then it’s a root, and if it’s not requiring any node, then it’s a leaf. Loading goes from root to leaves, while execution goes the other way around.
  • Easy | Goon’s nodes are calls to the Goon() function, nodes’ results are available as entries of the goon repository, and the last tree’s result by means of the goony shortcut
  • Elegant | Goon provides a simple environment, where a node can access imported results by means of this, and export its own results by means of return
  • Flexible | Goon can load the same node many times in the same tree and its behavior can be tailored by specifying how each script is required: in link mode, in exec mode, or in redo mode
  • Flexible | Goon’s nodes can be used as roots for their subtrees, without any modification, simply by loading them directly from a page
  • Flexible | Gooned pages can load all the needed trees, each with its own root, and they can be completely independent or grafted
  • Flexible | Goon’s loading trees can be transplanted between hosts, for example from localhost to a deployment host, by changing the query string used for loading Goon itself
Minimal Glossary
  • global script: a script not adjusted to work in a Goon setup
  • local script: a script that works in a Goon setup
  • gooned script: a call to Goon(), where each argument is specified
Basic Example

This example is bundled to Goon and all of its files are in the link folder.

  • main makes a message for showing the elapsed milliseconds between two times
  • script computes the difference between two values
  • script1 and script2 save the current time
  • main depends upon script, which depends upon script1 and script2, which both do not depend upon other scripts

Here are the scripts, in the order they will be loaded.

main:

Goon( "goon/1.1/link/", "main.js",
{ 
	scr: "script.js" 
}, 
function() { 
	alert( "main" );

	var msg = 'lap( ' 
		+ this.scr.n1.valueOf() + ', ' 
		+ this.scr.n2.valueOf() + ' ) = ' 
		+ this.scr.diff;

	return {msg: msg};
} );

script:

Goon( "goon/1.1/link/", "script.js",
{
	scr1: "script1.js",
	scr2: "script2.js"
},
function() { 
	alert( "script" );

	var a = this.scr1.value;
	var b = this.scr2.value;
	var c = b - a;

	return { n1: a, n2: b, diff: c };
} );

script1:

Goon( "goon/1.1/link/", "script1.js",
{}, 
function() { 
	alert( "script1" );

	var a = new Date();

	return {value: a};
} );

script2:

Goon( "goon/1.1/link/", "script2.js",
{}, 
function() { 
	alert( "script2" );

	var b = new Date();

	return {value: b};
} );

Goon() signature
  • the 1st argument is the prefix to prepend to all the paths in the 2nd and 3rd arguments
  • the 2nd argument is the path of the current script
  • the 3rd argument is the dependencies object, whose name/value pairs declare the files the local script requires
  • the 4th argument is a function declaration whose body is the local script
  • a local script must always return (export) an object
Import / Export

A local script imports the objects exported by its required scripts and exports an object (which can be empty) to the script that required it.

A local script exports its object by returning it, wherever it needs to return of course, and as many times as needed. On the other hand it gets access to the objects returned by all its required scripts by means of this, which is an object like the dependencies object, where the property names are the same but the values are the exported objects.

Shortcuts

When a gooned script is loaded, Goon() creates a Goon object out of it and adds the object to the global goon repository, under a property whose name is the script URI. The most useful property of the Goon object is exported, which is the object returned by the local script.

This means that this is just a convenient set of shortcuts inside the local script for accessing the exported objects of its children. But in general, for accessing the exported object of a gooned script already loaded and executed, you must use something like goon[ scriptUri ].exported, from any local or global script.

For example, you could use that method from the page where the loading process starts by linking the root of a Goon tree. In this case, though, it’s much more convenient another shortcut: goony, which always references the exported object of the root of the last Goon tree. So, back to the example, the links in the following page show the same value

page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title> link test </title>
<script type="text/javascript" src="goon-1.1.js"></script>
<script type="text/javascript" src="link/main.js"></script>
</head>
<body>
<p><a href="javascript:alert( 
goon['http://localhost/test/goon/1.1/link/main.js']
.exported.msg 
);">test goon</a></p>
<p><a href="javascript:alert( 
goony.msg
);">test goony</a></p>
</body>
</html>

test it now

Executions

If you open the above page in a browser, you’ll see an alert when each local script is being executed. The expected sequence is “script1”, “script2”, “script”, “main”. Each alert will generate a delay between the scripts that you can easily verify when all is done by clicking the links on the page.

Here is a picture of the DOM shown by Firebug upon completion:

link.png

test.png

Modes

What are all these link words for? Look at the above image. There is a mode property with a link value. That is the reason there are so many link words here. In fact, mode is a property whose value belongs to the enumeration { “link”, “exec”, “redo” } and defaults to link. So this Basic Example is pretty much the same as the Link Example, except for being able to see the link mode at work.

The mode property should always be carefully considered, when declaring a dependency, because it rules the case when the required script has been loaded before by Goon.

  • link: everything is bypassed; neither the required scripts are loaded again nor its local script is executed again; the exported object is a link to the exported object of the first script with such an URI
    Link Example
  • exec: the required scripts are bypassed, but its local script is executed again; the imported objects are links to the imported objects of the first script with such an URI
    Exec Example
  • redo: nothing is bypassed; the required scripts are loaded again and its local script is executed again; dependencies loading is in accordance to their respective modes
    Redo Example

The mode must be specified by the gooned script when declaring a dependency, like here:

{
	scr1: { file: "exec/script1.js", mode: "exec" },
	scr2: "exec/script2.js"
}

which is exactly the same as here (due to the default mechanism):

{
	scr1: { file: "exec/script1.js", mode: "exec" },
	scr2: { file: "exec/script2.js", mode: "link" }
}

Query String

For transplanting a tree from one host to another, for example for moving a Goon’s application from test to production, the best method is to enable the base parameter in the query string of the URI used for loading Goon in a page.

The base parameter should be set to the prefix that is going to change from one implantation to the other. Compare the previous page with this one

page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title> link test </title>
<script type="text/javascript" src="goon-1.1.pack.js?base=http://localhost/test/" id="g8n"></script>
<script type="text/javascript" src="link/main.js"></script>
</head>
<body>
<p><a href="javascript:alert( 
goon['http://localhost/test/goon/1.1/link/main.js']
.exported.msg 
);">test goon</a></p>
<p><a href="javascript:alert( 
goony.msg
);">test goony</a></p>
</body>
</html>

As you see, I’ve set the base parameter to “http:// localhost / test /”, and I’ve added an id attribute to the script element, with the strange g8n value. An id with that value is needed if you want to enable the base feature. You make the gooned scripts take care of the base piece simply by erasing it from the first argument of the Goon() function, like here:

main:

Goon( "goon/1.1/link/", "main.js",
{ 
	scr: "script.js" 
}, 
function() { 
	alert( "main" );

	var msg = 'lap( ' 
		+ this.scr.n1.valueOf() + ', ' 
		+ this.scr.n2.valueOf() + ' ) = ' 
		+ this.scr.diff;

	return {msg: msg};
} );

These are the rules that apply when making up the URI for loading a gooned script:

  1. the base is used in general if the id=”g8n” is added to the script element that loads the Goon script into a page
  2. base is used if home + path of the gooned script does not contain “://”
  3. base is used up to the first “/” if home + path begins with a “/”
  4. base is used completely if home + path does not begin with a “/”
  5. then the URI of the gooned script is set to: base + home + path

Note that in the previous list, home and path refer to arguments of the Goon() function: home is the first parameter, and path is the second parameter or the file property of each of the required scripts in the third parameter.