Saturday, March 6, 2010

OOJS Framework; Package Structure

The FRAMEWORK singleton (core object) in my previous post was simply 'FRAMEWORK' in the global ns. This was so that any methods within the framework that needed to be called would be triggered simply by using FRAMEWORK.method() and any public availably object directly accessible on FRAMEWORK.

Of course we can follow a naming convention similar to the Java package structure, like com.domain.core.Framework (simply create com={},com.domain={},...),in which case, each time you wanted to reference the framework you would be required to type the unnecessarily long string.

Personally, I have not wanted to go use this convention, but understand the flexibility attained by using it. Instead, I use {frameworkName}.{util/type}.js for the file system name, and extend the FRAMEWORK object in js with the util/type name. An example is - FRAMEWORK.ajax.js for the file, FRAMEWORK.ajax for the ajax util.

In websites that I've worked on, the file system's package structure is usually:

/scripts/jquery/jquery.min.js
/scripts/core/framework.core.js
/scripts/ui/*
/scripts/thirdparty/*
/scripts/websites/*

Everything through /scripts/ is served up through a HTTP server (Apache) for speed as well as the ability to have the stream GZipped (compression) for performance optimization in the production environment.

The file structure is broken out such that the particular library that you are using contains all library related js files, as well as any plugins created for the library. In my case, jQuery is the library of choice, and so there is a dedicated jQuery directory.

Alongside the library dir, there is the CORE directory, for all core utility wrappers -- ajax, json, notifications, loggers, form, etc. A separate directory is also maintained for all user interface components, which would in some cases ideally be lazy loaded (using a dependency injection method like jQuery.require). The downside of using this of course, is that the end user has to make another request to get the js file, as opposed to having it be available through a minified / compressed file already cached from the first hit on the server. Sometimes, due to the size of a framework, this is simply necessary.

Aside from the library, core and ui directories, there should also be a directory for 'thirdparty' tools/plugins, in an effort to separate code. This directory should house any third party tools brought in, that do not depend on a particular library - an example of this may be any given WYSIWYG/WYSIWYM - like WYMEditor, HTMLBox, TinyMCE, etc.

Finally, a separate directory for the websites utilizing the code. In some cases, the framework you are creating may only serve up one website, in which case this directory may be redundant. But that being said, I like the separation of code so that I can decouple core, required methods, from website implementation specific code; allowing for a more framework oriented style of development. Also, the naming convention for the website directory should be {website}.{util/type}.js, and should extend the framework object like: FRAMEWORK.{website}.

Here's an example of how I have things setup right now --
jQuery
/scripts/jquery/jquery-1.4.min.js
/scripts/jquery/jquery.dimensions.js

Core
/scripts/core/tikku.core.js
/scripts/core/tikku.logger.js
/scripts/core/tikku.ajax.js
/scripts/core/tikku.json.js
/scripts/core/tikku.form.js
/scripts/core/tikku.notify.js
/scripts/core/tikku.resources.js

UI
/scripts/ui/tikku.accordion.js
/scripts/ui/tikku.carousel.js
/scripts/ui/tikku.dialog.js
/scripts/ui/tikku.dragdrop.js
/scripts/ui/tikku.contextmenu.js
/scripts/ui/tikku.tabs.js

Third-Party
/scripts/thirdparty/swfobject.js

WebSites
/scripts/websites/tikku/tikku.core.js
/scripts/websites/admin/admin.core.js

The resulting DOM structure is -
TT
   | // core
   + -- log
       + debug(msg:String,opts:Object)
       + warn(msg:String,opts:Object)
       + error(msg:String,opts:Object)
   + -- ajax
       + send(opts:Object,async:Boolean)
       + sync(opts:Object)
       + asnc(opts:Object)
       + defaultOpts(Object)
   + -- json
   + -- form
   + -- notify
   + -- resources
   | // ui
   + -- accordion
   + -- carousel
   + -- dialog
   + -- dragdrop
   + -- contextmenu
   + -- tabs
   | // websites
   + -- tikku
   + -- admin // only avail if in 'admin' mode

I have given some insight into the contents of the log and ajax modules, but mainly wanted to illustrate the resulting structure after document.ready is fired.

Also note, global pointers can be created to point to the particular sub-level nodes. I like to have 'log' available within the global NS, so even though TT.log suffices, I create a global log var that points to TT.log. This allows me to use log.debug, log.warn and log.error w/ Firebug, similar to how I instantiate log4j in java.

Importing the framework


There are two environments that must be taken care of: (1) development and (2) production. In the development case, any given website requires a set of js files to include in the meta header. This list should be compressed for the production case. For compression, I recommend using Google's Closure Compiler, or Yahoo's Compressor (which works for CSS too).

It's worth noting that you will want to import your library's javascript BEFORE your framework's. You will also want to make sure to understand any dependencies for framework modules, as the order in which modules is imported dictates how they are initialized (remember the readyFunctionStack?).

Friday, March 5, 2010

OOJS Framework; The Core Global Object

So far I've introduced a design pattern for object oriented JavaScript and then I showed an example of the pattern in practice.

Now I would like to introduce the concept of a framework singleton that avoids clutter in the global namespace and simplifies management of JavaScript codebase.

This singleton must provide the following piece of functionality -
  1. Contained namespace
  2. Window onload stack
  3. Initialization method to allow modular extensions to the framework
  4. Public, Global attributes (such as a debugging flag, a DOM signature, etc)
  5. Global helpers, added as necessary

Skip the steps & get straight to the final product

Let's start:

The Global FRAMEWORK node

In my first post, I described two patterns, (a) create a singleton and (b) create a 'base' object like a java pojo. Applying the first pattern, creating a singleton, we essentially initialize the framework like so --

Note: FRAMEWORK is in the global ns in the example above. I would recommend, like most library/api conventions, you keep it short since you'll be referencing it.

The above example satisfies our first goal, containing the namespace so that any variables defined with the 'var' modifier, will be treated private to FRAMEWORK; allowing us to specify attributes that can only be overridden if public methods exist to access them.

FRAMEWORK's window onload stack

Often times events or utility init's require a loaded DOM. Rather than initializing the function immediately, we can store the init functions in local memory in an array and piggy back on a framework to perform the window onload event. This ensures a synchronous initialization of extended utils.

The array, readyFunctionStack is init'd as an array literal with the initFramework function as the internal init function. The initFramework() function is private, which ensures that the init method cannot be modified. The p.ready is a public method, which has a timer within it to display how long the framework initialization takes to init all the functions pushed to the stack.

The p.ready function is referenced as FRAMEWORK.ready, and should be passed onto document ready (window load) methods, like in jQuery: jQuery(document).ready(FRAMEWORK.ready), after the code for FRAMEWORK.

Initialization method for extending objects

The readyFunctionStack described earlier holds all the init functions to be initialized when the window is loaded. In order to modify the private stack, a public method is required to append the function to the array.

Global variables within FRAMEWORK

It's good to store constants/global variables within the framework in an object (hashmap equivalent). In the global object I think there are two attributes that are essential: a debug flag (boolean), i.e. if the framework is in debug mode & a framework signature (why? when interacting with iframes, the framework will be loaded in a different window. the stamp can be used to differentiate the windows). My proposal is shown below.

Finally, all of it together