NAME JSAN - JavaScript Archive Network SYNOPSIS // Or in a library. try { var jsan = new JSAN(); jsan.use('Some.Library'); } catch (e) { alert("Requires JSAN"); } DESCRIPTION This library allows JavaScript to behave more like traditional programming languages and offers the programmer the abilility to create well-designed, modular code. Constructor var jsan = new JSAN('/js'); Create a new "JSAN" object. A list of optional repository include paths may be given as constructor arguments. These arguments are added to "includePath" using "addRepository()". Class Properties globalScope JSAN.globalScope = _player; By default "globalScope" is set to the value of "self". This works great in web browswers, however other environments may not have "self" (an alias for "window" in web browswers) for a global context. In those cases, such as embedded JavaScript, you should reset "globalScope" before calling "use()". includePath JSAN.includePath = []; The "includePath" is a list of URLs that should be used as prefixes for libraries when they are "use()"d. If you are adding repositories please consider using the "addRepository()" method. errorLevel JSAN.errorLevel = "die"; By default the "errorLevel" is set to *none*. This will supress errors when trying to load libraries. *die* will throw an exception and it is the responsibility of the calling code to catch it. *warn* will use the "alarm()" function, usually present in web browsers, to alert a user on error. This is good for debugging (in web browsers). errorMessage var error = JSAN.errorMessage; This contains the text of any error, no matter what the "errorLevel". Inspect it to discover problems. Methods use() jsan.use('Test.Simple'); Download and import a library. There is a mapping that is done with the library name. It must be converted into a URL. Here is how it works. Test.Simple Test/Simple.js HTTP.Request HTTP/Request.js Foo.Bar.Baz Foo/Bar/Baz.js Each "includePath" is prepended to the libraries URL representation creating the full URL to the library. The library's constructor and prototype is imported into the calling context. You can also request certain functions, or groups of functions, to be imported explicitly. Here is an example of each. jsan.use('Test.More', 'plan', 'ok', 'is'); jsan.use('Digest.MD5', ':all'); addRepository() jsan.addRepository('js/private'); Add a path to "includePath". This will move the repository to the beginning of the list and it will be checked first for libraries. Compile Time "JSAN" creates an interesting sort of compile time for libraries loaded through it. This means that code that does not exist inside of a function is considered *compile time* code. This has certain implications when a library uses JSAN to import another library. Namespaces JavaScript - exempting version 2 anyway - does not have a notion of namespaces. For this reason something very important has been missing. However, JavaScript's object system is perfectly suited to create namespaces for well written code. The first thing you have to do when creating a library is define the namespace. The namespace must match the library name as well so "use()" can import the classes and functions correctly. The name of this library, "JSAN", or the name of our testing system, "Test.Simple", are exapmles of namespaces. A namespace is built by defining objects at each level until you reach the final name. Defining a namespace for JSAN is simple. var JSAN = {}; Defining the namespace for "Test.Simple" takes a little more work. First you have to create an object at the variable named "Test" if it doesn't already exist. Then you can create an object, or place a constructor, at the "Test.Simple" location. if (Test == undefined) var Test = {}; Test.Simple = {}; So far we've just been inserting blank objects at the final namespace. That's fine if your library implements functions and does not implement a class. However, if you implement a class you will want to place a constructor in the final namespace instead. if (Name == undefined) var Name = {}; Name.Space = function () { // This is the constructor. } Further, you'll want to define you class. This is done by defining the prototype in your namespace. Name.Space.prototype = { publicProperty: 'you see me', _privateProperty: 'boo', publicMethod: function (arg1, arg2) { // We do stuff man. }, _privateMethod: function () { this._privateProperty = "no peaking"; } }; Exporting The "use()" function supports an Exporter sytle system. This means that you can create functions that will be exported to the caller automatically, functions that will be exported when requested, and groups of functions - called tags - that are exported when requested. EXPORT Set up functions that are auto exported unless the caller declares a function list. Name.Space.EXPORT = [ 'functionOne', 'functionTwo' ]; Importing the default functions. jsan.use('Name.Space'); Importing specific functions. jsan.use('Name.Space', 'functionOne'); // Don't want functionTwo() EXPORT_OK Set up funcitons that can be exported but only by request. Name.Space.EXPORT = [ 'functionOne', 'functionTwo' ]; Name.Space.EXPORT_OK = [ 'specialOne', 'specialTwo' ]; Import the default functions. This will not import any functions inside the "EXPORT_OK" list. jsan.use('Name.Space'); Import some specific function from "EXPORT_OK". This will not import any functions from the "EXPORT" list. jsan.use('Name.Space', 'specialOne'); EXPORT_TAGS Set up a grouping of functions that can be exported all at once. This example also illustrates something I dislike about JavaScript arrays. I'll leave that for you to discover. function _expandTheFreakingLists () { var expanded = []; for (var i = 0; i <= arguments.length; i++ ) { var list = arguments[i]; for (var j = 0; j <= list.length; j++) { expanded.push(list[j]); } } return expanded; } Name.Space.EXPORT_TAGS = { ":common": Name.Space.EXPORT, ":all": _expandTheFreakingLists(Name.Space.EXPORT, Name.Space.EXPORT_OK) }; Now import all the functions. jsan.use('Name.Space', ':all'); Import the common functions and one special one. jsan.use('Name.Space', ':common', 'specialOne'); Writing Libraries Class Libraries Class libraries implement a public class that will be exported to the caller. The public class contains the same name as the package "use()" was called with. First, you have to set up a namespace. if (DOM == undefined) DOM = {}; DOM.Display = function () {}; Next you can define the class's prototype. DOM.Display.prototype = { register: {}, hideElement: function (id) { // Do stuff. }, showElement: function (id) { // Do stuff. }, showOnlyElement: function (id) { // Do stuff. }, registerElement: function (id) { // Do stuff. } }; At this point your library may have dependences. If that's the case you can use "JSAN" to try and import them. This works because you have, theoretically, modified the "JSAN.includePath" on your initial invocation of the library. So your libraries being "use()"d need not know anything about "includePath"s. try { var jsan = new JSAN; jsan.use('Some.Dependency'); } catch (e) { if (DOM.Display.DEBUG != undefined) alert(e.message); } Functional Libraries Functional libraries strictly intend to export a set of functions to the caller. While they may contain a class, or many classes, those classes are not part of the publicly documented API. These are simple to create but a few rules do apply. First, you still have to set up a namespace. if (Digest == undefined) var Digest = {}; Digest.MD5 = {}; Next you can define your export list. Digest.MD5.EXPORT_OK = [ 'md5', 'md5Hex', 'md5Base64' ]; Digest.MD5.EXPORT_TAGS = { ':all': Digest.MD5.EXPORT_OK }; Now you may go on to define your functions. They must be fully qualified just like the "EXPORT_OK" and "EXPORT_TAGS" variables. Digest.MD5.md5 = function (str) { // Do stuff. } Digest.MD5.md5Hex = function (str) { // Do stuff. } Digest.MD5.md5Base64 = function (str) { // Do stuff. } At this point your library may have dependences. If that's the case you can use "JSAN" to try and import them. try { var jsan = new JSAN; jsan.use('Some.Dependency'); } catch (e) { if (Digest.MD5.DEBUG != undefined) alert(e.message); } SEE ALSO JavaScript Namespaces, . Original JSAN Brainstorming, . OpenJSAN, . Signed JavaScript, . AUTHOR Casey West, . COPYRIGHT Copyright (c) 2005 Casey West. All rights reserved. This module is free software; you can redistribute it and/or modify it under the terms of the Artistic license.