Provides a module, modules.js, with the following features:
- module loading
- singleton modules (module object loaded and executed only the first time it's required).
- module-relative module URL references
A module is a JavaScript file. modules.js uses an XHR (HTTP request) to get the file's text, the eval function to run the script, and with blocks to manage the scope chain in the script.
These tools permit modules.js to load scripts only once and on demand, to completely avoid touching the global name space (no added members to the window object), to give every script its own private name space, and to insert names in a module's scope ("importing" using include).
modules.js also masks its own existence from other scripts by removing it's <script> tag from the <head> of the containing HTML document. The module loader then detects the earliest possible moment after the document's DOM has fully loaded, presumably before the document has been rendered, then loads a list of ampersand delimited modules from the query modules.js query string:
<script src="modules.js?includeMe.js"></script>
A single page can have more than one self-contained module loader, with its own cache of modules. Each module environment would be completely unaware of the others.
modules.js "launders" the scope chain so that every module has a safe and clean execution environment. The scope chain in each module includes, from bottom to top:
- the global object (window)
- the module scope, which contains functions for loading other modules (include, require), convenience functiosn for logging to a cross-browser debuging consoles (log, warn, error), the module, and items loaded from other modules by include.
- the module object. Module scripts can export items by making them members of the module.
- an empty closure (an "enclosure"). Variables declared with var inside a module script are only accessible inside the script.
The context object, this, is the module inside a module script.
modules.js also bootstraps these modules:
- modules.js provides access to the module modulesjs internal functions and data.
- http.js provides synchronous and synchronous, cross-platform XML HTTP request wrappers and URL parsing and formatting.
- environment.js provides information about the Javascript execution environment discovered while bootstrapping the module modulesjs, like Windows/Mac/Linux, Browser/Rhino/Spidermonkey/Dashboard Firefox/Explorer/Safari/Opera.
- browser.js provides cross platform element inspection rudiments.
module scopes contain special bound copies of functions from the modules.js module and the environment.js module. Functions with these names are available in the module scope of every module and are aware of the module that they were called from. This permits them to perform behaviors like recording the URL of the module that called them or resolve URL arguments relative to the module that they are called in.
modules.js.logModule L305
- accepts:
- a module URL String
- an optional depth, noting how many occurences of require closures are on the JavaScript execution stack.
logs that a module at a given URL has been loaded. This function simply calls log, but can be overriden in other modules. This function does not check whether logging is turned on; that's checked by require.
resolves a URL Object relative the calling module or modules.js. URLs beginning with . imply relation to the current module file.
URL Objects are produced by http.js#resolveObject and have values for all of the http.js#keys.
decorates an object with attributes for their:
- modules.js#moduleUrl,
- modules.js#name, and
- modules.js#fullName
denoting the module that it is from or bound to and the name it has in that scope.
returns a Module for a given moduleUrl:
var module = require(moduleUrl);
blocks until the module has been loaded or the module object has been retrieved from the cache of singleton module objects.
A moduleUrl is a String that describes the location of a JavaScript file relative to the modulesUrl, the path leading up to modules.js in the src attribute of its HTML script tag. If the moduleUrl begins with a dot (.), the moduleUrl is resolved relative to the URL of the current Module. Thus:
var http = require('http.js');
retrieves the http.js module that sits beside modules.js on the server. However, in browser/layout.js:
var html = require('./html.js');
retrieves the browser/html.js module.
supports optional continuation passing style:
require(moduleUrl, function (module) {
});
If a moduleUrl and a continuation are provided, require returns undefined and instead passes the Module to the continuation. If an exception is thrown during the execution of require, require will call the continuation with undefined as the first argument and the Error as a second argument.
Currently, require blocks until the continuation finishes executing. However, in future versions, the continuation passing style may be asynchronous.
modules.js.include L584
accepts a moduleUrl, returns a Module, and copies the items from the Module into the current ModuleScope except for the moduleScope item since this would mask the current module's scope:
var base = require('base.js'); include('base.js'); assert(base.Set == moduleScope.Set == Set);include does not support continuation passing style.
include supports an alternate argument. If you provide include with an arbitrary object instead of a Module, it will copy the items of the object into the current ModuleScope instead. This object can be a Module:
include({ 'x': 10; }); assert(x == 10);include also marks any imported functions with their foreign module's URL and name using dub.
include doesn't work for modules.js. Use require instead. This is just a safety to prevent you from calling functions from modules.js that haven't been bound to the current module. If you were able to include items from modules.js in your ModuleScope, using include thereafter would fail since the version of include in modules.js is not bound to the current Module and cannot copy items into its ModuleScope.
copies items from an object onto the current Module. This is handy for exporting items from another module, since include does not do this implicitly as import would in Python.
accepts an Object or Module and returns undefined.
This function takes special care not to replace the calling module's moduleScope object.
modules.js.register L683
replaces a Module in the singleton module cache with a given object.
- accepts:
- a moduleUrl
- a Module, a Function, or some other Object.
- an optional boolean value indicating whether to force a reload of the module next time it's required or included.
If you provide a Module, the Module replaces the current module in the cache.
If you provide a Function, register treats the function as a constructor function for the a Module on the given URL. The function will be called with a Module object as the context object (this) when it's required. This particular feature is important for the module bundling step of the Chiron build process and permits multiple modules to be registered in a single file.
modules.js.foreignModuleBind L724
a decorator for functions that should receive the Module they're included in and called from as their context object (this)
For example, if in module a.js we declare a logModule exportable function with the foreignModuleBind decorator:
this.logModule = foreignModule(function () { log(this.moduleScope.moduleUrl); });And we include and call logModule in b.js:
include('a.js'); logModule();logModule will receive the Module for b.js as this instead of the global context object (window) or the a.js Module. Thus, b.js would print to the console:
b.js
evaluates given text in global scope, such that evaluated scripts do not have an opportunity to accidentally access or manipulate anything but global variables, using evalGlobal, internally.
evalGlobalWith accepts some optional scope objects to evaluate the text within, so the user has the option of inserting variables for the script's use.
Usage:
evalGlobalWith(text)
function () { eval(text) }.apply(this, arguments)
evalGlobalWith(text, scope)
with (scope) {
function () { eval(text) }.apply(this, arguments) }}
evalGlobalWith(text, innerScope, outerScope)
with (outerScope) {
with (innerScope) {
function () { eval(text) }.apply(this, arguments) }}}
evalGlobalWith(text, innerScope, ..., outerScope)
a version of eval that guarantees that only global names are available inside the given script. evalGlobal blocks until the evaluation is complete and returns the last value evaluated. evalGlobal exists in response to the need to protect variables in the calling scope. eval permits the given program to access the caller's scope chain:
(function () {
var a = 10;
eval("a = 20");
a == 20;
var b = 10;
evalGlobal("b = 20");
b == 10;
window.b == 20;
})();
The arguments object is a good way to pass and access variables inside of an evalGlobal program:
evalGlobal("arguments[1]", 10) == 10;
environment.js L878
environment.js.messages L882
environment.js.log L886
- accepts:
- a message and
- an optional label.
The label, by convention, is one of "log"`, "info", "warn", or "error". Custom loggers treat labels like "module", "pass", or "fail". Attempts to write the message to window.console, progressively handling console implementations that provide a function for the given label, or defaulting to log depending on availability.
Also adds a [message, label, moduleUrl] array to the end of environment.messages. label is one of "log", "warn", "info", or "error" by convention. moduleUrl is the URL of the module from which log was called.
In Safari, log writes to the Javascript debug console, which is only available if you set the preference:
defaults write com.apple.Safari IncludeDebugMenu 1In Firefox, you can get a debug console with Firebug, http://getfirebug.com.
You can override the behavior of log by assigning a different function to require('environment.js').log in any module.
Chiron can create a debug console for the purpose of unit testing or page debugging. To debug a web page, use modules.js to include debug.js on a page. To run a unit test, view run.html, lite.html, or edit.html with the moduleUrl of the unit test as a query string.
- see:
- error
- warn
- info
writes a warning message to window.console.warn or defers to log.
writes an error message to window.console.error or defers to log. Accepts a String or an Error.
writes an informational message to window.console.info or defers to log depending on the console.
environment.js.isBrowser L1027
environment.js.isDashboard L1029
environment.js.isRhino L1031
environment.js.isSpiderMonkey L1033
environment.js.isOpera L1051
environment.js.isKonqueror L1053
environment.js.isWebKit L1055
environment.js.isSafari L1057
environment.js.isKhtml L1059
environment.js.isGecko L1065
environment.js.isFirefox L1072
environment.js.isIE L1074
environment.js.isIE5 L1076
environment.js.isIE5_5 L1078
environment.js.isIE6 L1080
environment.js.isIE7 L1082
''a.k.a., AJAX''
Uses regular expressions designed by Steven Levithan [1].
http.js L1174
http.js.strictExpression L1223
http.js.Request.toString L1632
http.js.Request.getResponse L1636
http.js.Request.setHeader L1706
http.js.Request.isOpen L1712
http.js.Request.isSent L1718
http.js.Request.getTimeout L1724
http.js.Request.setTimeout L1730
http.js.Request.open L1736
Accepts
- method, an HTTP request method, for example, GET, POST, PROPFIND and others.
- url, a web location string
- synchronous, whether send will block until completed, for example, http.synchronous, http.asynchronous.
- user, an optional HTTP user name.
- password, an optional HTTP password.
http.js.Request.abort L1788
returns whether a request had a valid response. This usually is indicative of a 200 HTTP response code, but there are variations among browsers.
HTTP Status codes in the interval [200, 300] are all legal HTTP Ok responses.
In Firefox and Safari 3, local files acquired with an HTTP request have a status code of 0.
In Safari 2, local files acquired with an asynchronous HTTP request have a status of undefined.
In Safari, a response with no content causes a status of undefined.
Konqueror requires acceptance of 304, "using cache", according to dojo/src/hostenv_browser.js
http.js.Response.isError L1939
http.js.Response.getText L1945
http.js.Response.getXml L1951
http.js.Response.getDocument L1962
http.js.Response.getHeader L1968
http.js.Response.hasHeader L1974
http.js.Response.getHeaders L1980
http.js.Response.getLength L1988
As a final pass, modules.js accepts an optional, ampersand-delimited list of module URL's specified in the modules.js query string. When the module loader is ready and the DOM and CSS are fully loaded, it automatically uses require to load these modules. If you do not take advantage of this list, there is now way to use the module system since it leaves no toe-hold in global scope. The best practice is to at least require a module specific to your page, or use chiron.js to enable the use of namespaced inline scripts.
| [1] | http://stevenlevithan.com Steven Levithan. Provides regular expressions for both loose and strict URL parsing. |