The Gravey 2.0 Framework

grvClass.js

Summary

Collection of routines to emulate Java-like classes in JavaScript. A detailed explanation of the functionality of this file can be found in the AJAX from Scratch series of articles.

Version: 2.0

Requires:

Author: Bruce Wallace (PolyGlotInc.com)


Method Summary
static Function Class( <Function> theClass, <StringArray> optConstructorArgDescArray )
           Declare a class and create the glue objects and code to allow "pretty" source; A mechanism is set up to insure that required constructor parameters each have a defined value; This function also defines the "name" attribute of the specified class and initializes it with the class name.
static Function grvExtends( <Function> superClass )
           This utility function takes (via "THIS") a "class" and (re)sets its superClass.
static String grvFuncName(<Function> f)
           This function returns the name of a given function; It does this by using a regular expression to extract the function name from the function source code.

// This file uses JSDoc-friendly comments [ http://jsdoc.sourceforge.net/ ]

/**
 * @file         grvClass.js
 * @fileoverview Collection of routines to emulate Java-like classes in JavaScript. 
 * A detailed explanation of the functionality of this file can
 * be found in the <a target="_blank"
 * href="http://www.polyglotinc.com/AJAXscratch/Class">AJAX from Scratch</a>
 * series of articles.
 *
 * @author       Bruce Wallace  (PolyGlotInc.com)
 * @version      2.0
 * @requires     grvValidate.js (optional; only required if validating constructor args)
 */

//////////////////////////////////////////////////////////////////
// GRAVEY LEXICAL CODING CONVENTIONS:
// (*) All private variables or functions start with "_"
// (*) All variables and functions start with a lowercase letter
// (*) All Classes start with an uppercase letter
// (*) All Class methods and instance variables start with lowercase
// (*) All Class "static" methods and variables start with uppercase
// (*) All constants start with "k"
// (*) All global variables start with "g"
// (*) All event handler functions start with "on"
//
// (*) All Gravey utility global variables start with "gGrv"
// (*) All Gravey MVC     global variables start with "gMVC"
// (*) All Gravey EDO     global variables start with "gEDO"
// (*) All Gravey utility functions start with "grv"
// (*) All Gravey MVC     functions start with "mvc"
// (*) All Gravey MVC event handler functions start with "onMVC"
// (*) All Gravey EDO event handler functions start with "onEDO"
// (*) All Gravey MVC classes start with "MVC"
// (*) All Gravey EDO classes start with "EDO"
//////////////////////////////////////////////////////////////////

/**
 * This function returns the name of a given function; It does this by
 * using a regular expression to extract the function name from the
 * function source code.
 * @param {Function} f reference to a function
 * @return name of given function
 * @type String
 * @author Bruce Wallace (PolyGlotInc.com)
 * @version 2.0
 */
function grvFuncName(f)
{
	var s = f.toString().match(/function\s*(\S*)\s*\(/)[1];
	return s ? s : "anonymous";
}

Class(GrvObject);
/**
 * @class This is the root class for all "classes" implemented
 * via the {@link GLOBALS#Class} function (ala Java "Object").
 * Class replaces the normal Javascript "constructor" function
 * with a new wrapper function that implements a robust
 * inheritence mechanism.
 * This mechanism supports "pretty" source code whereby
 * "methods" are declared inline, but the source code of
 * those methods are not copied into every object instance
 * as would otherwise be the case.<p>
 * The wrapper function also sets up a real "constructor"
 * that can use {@link GLOBALS#grvValidateArgs} to insure
 * that the required parameters are passed to it, throwing
 * an exception if not.<p>
 * The design pattern implemented by Class/Extends corrects
 * several problems that occur with the naive inheritence
 * mechanisms often used with Javascript.
 *<pre>
 *  The required coding convention for "GrvObject":
 *  (1) the "class" is declared via the utility
 *      function {@link GLOBALS#Class}
 *  (2) the "superclass" (if any) is declared via the
 *      .Extends method {@link GLOBALS#grvExtends}
 *  (3) the "constructor" logic for the class is placed
 *      in a "method" named "konstructor"
 *  (4) the konstructor method invokes the "super" constructor
 *      via its class name [rather than literally "super()"]
 *</pre>
 * @throws grvMissingArgException thrown by constructor wrapper
 * @see GLOBALS#Class
 * @see GLOBALS#grvExtends
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function GrvObject()
{
	/** Invokes the "super" version of whatever method that is calling souper.
	 * @return either the return value of the super method or null if no super exists
	 */
	this.souper = function() {
    var superMethod = this.souper.caller.souper;
		return (superMethod) ? superMethod.apply( this, arguments ) : null;
	}
}

/**
 * This utility function takes (via "THIS") a "class" and
 * (re)sets its superClass.
 * THIS VERSION SETS UP "super" ACCESS TO OVERRIDDEN METHODS.
 * @param {Function} superClass the "superclass constructor" function
 * @return a reference to the "class object"
 * @type Function
 * @see #Class
 * @see GrvObject
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function grvExtends( superClass )
{
	///////////////////////////////////////////////////////////////////
	// NOTE: At this point superClass should have already had its own
	// Class and Extends calls performed. I.E. "declare" superclasses
	// BEFORE those classes that extend them.
	///////////////////////////////////////////////////////////////////
	if (!superClass.baseClass) throw "Undeclared Super Class in .Extends";

	//get superclass exemplar but avoid infinite recursion on implied root class
	var superexemplar = (this==GrvObject) ? null
					  : (superClass ? superClass.prototype : null);

	//Recall that one can't change the exemplar.__proto__ property directly
	//SO, one must create a new exemplar instance after baseClass.prototype is set.
	this.baseClass.prototype = superexemplar;
	var exemplar = new this.baseClass();

	//for each method in exemplar, if there is a different method with the
	//same name in superexemplar, setup the .souper link between them.
	var s,m;
	if (superexemplar)
	  for (var property in exemplar)// iterate over all properties
	  {
    	m =      exemplar[property];
    	s = superexemplar[property];

		if (s && (s!=m) && (m instanceof Function) && (s instanceof Function))
		  m.souper = s;
	  }

	//Note that it would normally be a problem if Extends were called after
	//someone has already created an instance of "this" class (via new).
	//However, that situation should never occur since all the calls to Class()
	//and Extends() are normally done at web page load time, and application
	//logic should only start after the page "onload" event, hence all these
	//exemplars should be stable.
	this.prototype             = exemplar;
	this.prototype.name        = this.cname;//default value

	// while "souper" works with konstructor, some may still prefer to
	// use the superclass name explicitly when invoking its constructor
	// so we leave this in to continue support for that.
	this.prototype[this.cname] = this.prototype.konstructor;
	return this;
}

/**
 * Declare a class and create the glue objects and code
 * to allow "pretty" source; A mechanism is set up to insure
 * that required constructor parameters each have a defined value;
 * This function also defines the "name" attribute of the specified
 * class and initializes it with the class name.
 * @param {Function} theClass the "class constructor" function
 * @param {StringArray} optConstructorArgDescArray optional array
 * of strings describing required parameters for this class'
 * "constructor"; Note: Optional constructor parameters should
 * not be included in this array.
 * @return the "class object" (an extended Function object)
 * @type Function
 * @see #Extends
 * @see #grvValidateArgs
 * @see GrvObject
 * @author Bruce Wallace  (PolyGlotInc.com)
 * @version 2.0
 */
function Class( theClass, optConstructorArgDescArray )
{
	// The "class" as passed in will really be an abstract base class. We
	// will squirrel that away and replace theClass with a wrapper function
	// so that method source code is not copied into each object instance
	// as would otherwise be the case.
	var baseClass = theClass;

	// create the new wrapper function to replace theClass
	var  className  = grvFuncName( theClass );
	self[className] = theClass = function(){
		var C = arguments.callee;
		if (C.required && self["grvValidateArgs"]!==void 0)
		  grvValidateArgs( C.cname, C.required, arguments );
		C.prototype.konstructor.apply( this, arguments );
	}
	theClass.required  = optConstructorArgDescArray;
	theClass.cname     = className;//FYI,netscape&firefox break with ".name"
	theClass.baseClass = baseClass;
	theClass.Extends   = grvExtends;	//so we can call grvExtends as a method
	return theClass.Extends(GrvObject);	//required here even if overridden later
}

The Gravey 2.0 Framework

Documentation generated by JSDoc on Sat Dec 8 21:51:44 2007