diff --git a/index.html b/index.html
index 9576e86..648996a 100644
--- a/index.html
+++ b/index.html
@@ -120,7 +120,7 @@
-
+
diff --git a/scripts/ease.js b/scripts/ease.js
new file mode 100644
index 0000000..5486fa2
--- /dev/null
+++ b/scripts/ease.js
@@ -0,0 +1,4987 @@
+/**
+ * Combined redistributable ease.js file
+ *
+ * For the original, uncombined and unminifed source, please visit
+ * http://easejs.org.
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * ease.js namespace
+ *
+ * All modules will be available via this namespace. In CommonJS format, they
+ * were accessed via the require() function. For example:
+ *
+ * var util = require( 'easejs' ).Class;
+ *
+ * In this file, the above would be written as:
+ *
+ * var util = easejs.Class;
+ *
+ * @type {Object}
+ */
+var easejs = {};
+
+( function( ns_exports )
+{
+ /**
+ * CommonJS module exports
+ *
+ * Since this file contains all of the modules, this will be populated with
+ * every module right off the bat.
+ *
+ * @type {Object.}
+ */
+ var module = {};
+
+ /**
+ * Returns the requested module
+ *
+ * The require() function is likely unavailable client-side (within a web
+ * browser). Therefore, we mock one. If it is available, this overwrites it.
+ * Our modules are all preloaded in the exports object.
+ *
+ * @param {string} module_id id of the module to load
+ *
+ * return tag intentionally omitted; too many potential return types and
+ * setting return type of {*} will throw warnings for those attempting to
+ * treat the return value as a function
+ */
+ var require = function( module_id )
+ {
+ // remove the './' directory prefix (every module is currently included
+ // via a relative path), stupidly remove ../'s and remove .js extensions
+ var id_clean = module_id.replace( /^\.?\/|[^/]*?\/\.\.\/|\.js$/, '' );
+
+ // attempt to retrieve the module
+ var mod = module[ id_clean ];
+ if ( mod === undefined )
+ {
+ throw "[ease.js] Undefined module: " + module_id;
+ }
+
+ return mod.exports;
+ };
+
+/** prop_parser **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Property keyword parser module
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+/**
+ * Known (permitted) keywords
+ * @type {Object.}
+ */
+var _keywords = {
+ 'public': true,
+ 'protected': true,
+ 'private': true,
+ 'static': true,
+ 'abstract': true,
+ 'const': true,
+ 'virtual': true,
+ 'override': true,
+ 'proxy': true
+};
+
+
+/**
+ * Parses property keywords
+ *
+ * @param {string} prop property string, which may contain keywords
+ *
+ * @return {{name: string, keywords: Object.}}
+ */
+exports.parseKeywords = function ( prop )
+{
+ var name = prop,
+ keywords = [],
+ keyword_obj = {};
+
+ prop = ''+( prop );
+
+ // only perform parsing if the string contains a space
+ if ( / /.test( prop ) )
+ {
+ // the keywords are all words, except for the last, which is the
+ // property name
+ keywords = prop.split( /\s+/ );
+ name = keywords.pop();
+
+ var i = keywords.length,
+ keyword = '';
+
+ while ( i-- )
+ {
+ keyword = keywords[ i ];
+
+ // ensure the keyword is recognized
+ if ( !_keywords[ keyword ] )
+ {
+ throw Error(
+ "Unexpected keyword for '" + name + "': " + keyword
+ );
+ }
+
+ keyword_obj[ keyword ] = true;
+ }
+ }
+
+ return {
+ name: name,
+ keywords: keyword_obj
+ };
+}
+} )( module['prop_parser'] = {}, '.' );
+/** util **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Contains utilities functions shared by modules
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+var propParseKeywords = require( __dirname + '/prop_parser' ).parseKeywords;
+
+
+/**
+ * Whether we can actually define properties, or we need to fall back
+ *
+ * This check actually attempts to set a property and fails if there's an error.
+ * This is needed because IE8 has a broken implementation, yet still defines
+ * Object.defineProperty for use with DOM elements. Just another day in the life
+ * of a web developer.
+ *
+ * This test is only performed once, when the module is first loaded. Don't
+ * expect a performance hit from it.
+ *
+ * @type {boolean}
+ */
+var can_define_prop = ( function()
+{
+ if ( typeof Object.defineProperty === 'function' )
+ {
+ try
+ {
+ // perform test, primarily for IE8
+ Object.defineProperty( {}, 'x', {} );
+ return true;
+ }
+ catch ( e ) {}
+ }
+
+ return false;
+} )();
+
+
+/**
+ * Freezes an object if freezing is supported
+ *
+ * @param {Object} obj object to freeze
+ *
+ * @return {Object} object passed to function
+ */
+exports.freeze = ( typeof Object.freeze === 'function' )
+ ? Object.freeze
+ : function( obj )
+ {
+ return;
+ }
+;
+
+
+/**
+ * Gets/sets whether the system needs to fall back to defining properties in a
+ * normal manner when use of Object.defineProperty() is requested
+ *
+ * This will be set by default if the JS engine does not support the
+ * Object.defineProperty method from ECMAScript 5.
+ *
+ * @param {boolean=} val value, if used as setter
+ *
+ * @return {boolean|Object} current value if getter, self if setter
+ */
+exports.definePropertyFallback = function( val )
+{
+ if ( val === undefined )
+ {
+ return !can_define_prop;
+ }
+
+ can_define_prop = !val;
+ exports.defineSecureProp = getDefineSecureProp();
+
+ return exports;
+};
+
+
+/**
+ * Attempts to define a non-enumerable, non-writable and non-configurable
+ * property on the given object
+ *
+ * If the operation is unsupported, a normal property will be set.
+ *
+ * @param {Object} obj object to set property on
+ * @param {string} prop name of property to set
+ * @param {*} value value to set
+ *
+ * @return {undefined}
+ */
+exports.defineSecureProp = getDefineSecureProp();
+
+
+/**
+ * Clones an object
+ *
+ * @param {*} data object to clone
+ * @param {boolean=} deep perform deep clone (defaults to shallow)
+ *
+ * @return {*} cloned object
+ *
+ * Closure Compiler ignores typeof checks and is thusly confused:
+ * @suppress {checkTypes}
+ */
+exports.clone = function clone( data, deep )
+{
+ deep = !!deep;
+
+ if ( data instanceof Array )
+ {
+ if ( !deep )
+ {
+ // return a copy of the array
+ return data.slice( 0 );
+ }
+
+ // if we're performing a deep clone, we have to loop through each of the
+ // elements of the array and clone them
+ var ret = [];
+ for ( var i = 0, len = data.length; i < len; i++ )
+ {
+ // clone this element
+ ret.push( clone( data[ i ], deep ) );
+ }
+
+ return ret;
+ }
+ else if ( typeof data === 'function' )
+ {
+ // It is pointless to clone a function. Even if we did clone those that
+ // support toSource(), they'd still do the same damn thing.
+ return data;
+ }
+ // explicitly testing with instanceof will ensure we're actually testing an
+ // object, not something that may be misinterpreted as one (e.g. null)
+ else if ( data instanceof Object )
+ {
+ var newobj = {},
+ hasOwn = Object.prototype.hasOwnProperty;
+
+ // copy data to the new object
+ for ( var prop in data )
+ {
+ if ( hasOwn.call( data, prop ) )
+ {
+ newobj[ prop ] = ( deep )
+ ? clone( data[ prop ] )
+ : data[ prop ]
+ ;
+ }
+ }
+
+ return newobj;
+ }
+
+ // primitive type; cloning unnecessary
+ return data;
+};
+
+
+/**
+ * Copies properties from one object to another
+ *
+ * This method is designed to support very basic object extensions. The
+ * destination argument is first to allow extending an object without using the
+ * full-blown class system.
+ *
+ * If a deep copy is not performed, all values will be copied by reference.
+ *
+ * @param {Object} dest destination object
+ * @param {Object} src source object
+ * @param {boolean} deep perform deep copy (slower)
+ *
+ * @return {Object} dest
+ */
+exports.copyTo = function( dest, src, deep )
+{
+ deep = !!deep;
+
+ var get, set, data;
+
+ // sanity check
+ if ( !( dest instanceof Object ) || !( src instanceof Object ) )
+ {
+ throw TypeError(
+ "Must provide both source and destination objects"
+ );
+ }
+
+ // slower; supports getters/setters
+ if ( can_define_prop )
+ {
+ for ( var prop in src )
+ {
+ data = Object.getOwnPropertyDescriptor( src, prop );
+
+ if ( data.get || data.set )
+ {
+ // Define the property the slower way (only needed for
+ // getters/setters). We don't have to worry about cloning in
+ // this case, since getters/setters are methods.
+ Object.defineProperty( dest, prop, data );
+ }
+ else
+ {
+ // normal copy; cloned if deep, otherwise by reference
+ dest[ prop ] = ( deep )
+ ? exports.clone( src[ prop ], true )
+ : src[ prop ]
+ ;
+ }
+ }
+ }
+ // quick (keep if statement out of the loop)
+ else
+ {
+ for ( var prop in src )
+ {
+ // normal copy; cloned if deep, otherwise by reference
+ dest[ prop ] = ( deep )
+ ? exports.clone( src[ prop ], true )
+ : src[ prop ]
+ ;
+ }
+ }
+
+ // return dest for convenience (and to feel useful about ourselves)
+ return dest;
+};
+
+
+/**
+ * Parses object properties to determine how they should be interpreted in an
+ * Object Oriented manner
+ *
+ * @param {!Object} data properties with names as the key
+ *
+ * @param {!{each,property,method,getset,keywordParser}} options
+ * parser options and callbacks
+ *
+ * @return undefined
+ */
+exports.propParse = function( data, options )
+{
+ // todo: profile; function calls are more expensive than if statements, so
+ // it's probably a better idea not to use fvoid
+ var fvoid = function() {},
+ callbackEach = options.each || undefined,
+ callbackProp = options.property || fvoid,
+ callbackMethod = options.method || fvoid,
+ callbackGetSet = options.getset || fvoid,
+ keywordParser = options.keywordParser || propParseKeywords,
+
+ hasOwn = Object.prototype.hasOwnProperty,
+
+ parse_data = {},
+ name = '',
+ keywords = {},
+ value = null,
+ getter = false,
+ setter = false;
+
+ // for each of the given properties, determine what type of property we're
+ // dealing with (in the classic OO sense)
+ for ( var prop in data )
+ {
+ // ignore properties of instance prototypes
+ if ( !( hasOwn.call( data, prop ) ) )
+ {
+ continue;
+ }
+
+ // retrieve getters/setters, if supported
+ if ( can_define_prop )
+ {
+ var prop_desc = Object.getOwnPropertyDescriptor( data, prop );
+ getter = prop_desc.get;
+ setter = prop_desc.set;
+ }
+
+ // do not attempt to retrieve the value if a getter is defined (as that
+ // would then call the getter)
+ value = ( typeof getter === 'function' )
+ ? undefined
+ : data[ prop ];
+
+ parse_data = keywordParser( prop ) || {};
+ name = parse_data.name || prop;
+ keywords = parse_data.keywords || {};
+
+ if ( options.assumeAbstract || keywords[ 'abstract' ] )
+ {
+ // may not be set if assumeAbstract is given
+ keywords[ 'abstract' ] = true;
+
+ if ( !( value instanceof Array ) )
+ {
+ throw TypeError(
+ "Missing parameter list for abstract method: " + name
+ );
+ }
+
+ verifyAbstractNames( name, value );
+ value = exports.createAbstractMethod.apply( this, value );
+ }
+
+ // if an 'each' callback was provided, pass the data before parsing it
+ if ( callbackEach )
+ {
+ callbackEach.call( callbackEach, name, value, keywords );
+ }
+
+ // getter/setter
+ if ( getter || setter )
+ {
+ callbackGetSet.call( callbackGetSet,
+ name, getter, setter, keywords
+ );
+ }
+ // method
+ else if ( ( typeof value === 'function' ) || ( keywords[ 'proxy' ] ) )
+ {
+ callbackMethod.call(
+ callbackMethod,
+ name,
+ value,
+ exports.isAbstractMethod( value ),
+ keywords
+ );
+ }
+ // simple property
+ else
+ {
+ callbackProp.call( callbackProp, name, value, keywords );
+ }
+ }
+};
+
+
+/**
+ * Only permit valid names for parameter list
+ *
+ * In the future, we may add additional functionality, so it's important to
+ * restrict this as much as possible for the time being.
+ *
+ * @param {string} name name of abstract member (for error)
+ * @param {Object} params parameter list to check
+ *
+ * @return {undefined}
+ */
+function verifyAbstractNames( name, params )
+{
+ var i = params.length;
+ while ( i-- )
+ {
+ if ( params[ i ].match( /^[a-z_][a-z0-9_]*$/i ) === null )
+ {
+ throw SyntaxError(
+ "Member " + name + " contains invalid parameter '" +
+ params[ i ] + "'"
+ );
+ }
+ }
+}
+
+
+/**
+ * Creates an abstract method
+ *
+ * Abstract methods must be implemented by a subclass and cannot be called
+ * directly. If a class contains a single abstract method, the class itself is
+ * considered to be abstract and cannot be instantiated. It may only be
+ * extended.
+ *
+ * @param {...string} def function definition that concrete
+ * implementations must follow
+ *
+ * @return {function()}
+ */
+exports.createAbstractMethod = function( def )
+{
+ var definition = Array.prototype.slice.call( arguments );
+
+ var method = function()
+ {
+ throw new Error( "Cannot call abstract method" );
+ };
+
+ exports.defineSecureProp( method, 'abstractFlag', true );
+ exports.defineSecureProp( method, 'definition', definition );
+ exports.defineSecureProp( method, '__length', arguments.length );
+
+ return method;
+};
+
+
+/**
+ * Determines if the given function is an abstract method
+ *
+ * @param {function()} func function to inspect
+ *
+ * @return {boolean} true if function is an abstract method, otherwise false
+ *
+ * @suppress {checkTypes}
+ */
+exports.isAbstractMethod = function( func )
+{
+ return ( ( typeof func === 'function') && ( func.abstractFlag === true ) )
+ ? true
+ : false
+ ;
+};
+
+
+/**
+ * Shrinks an array, removing undefined elements
+ *
+ * Pushes all items onto a new array, removing undefined elements. This ensures
+ * that the length of the array represents correctly the number of elements in
+ * the array.
+ *
+ * @param {Array} items array to shrink
+ *
+ * @return {Array} shrunken array
+ */
+exports.arrayShrink = function( items )
+{
+ // copy the methods into a new array by pushing them onto it, to ensure
+ // the length property of the array will work properly
+ var arr_new = [];
+ for ( var i = 0, len = items.length; i < len; i++ )
+ {
+ var item = items[ i ];
+ if ( item === undefined )
+ {
+ continue;
+ }
+
+ arr_new.push( item );
+ }
+
+ return arr_new;
+};
+
+
+/**
+ * Uses Object.getOwnPropertyDescriptor if available, otherwise provides our own
+ * implementation to fall back on
+ */
+exports.getOwnPropertyDescriptor =
+ ( can_define_prop && Object.getOwnPropertyDescriptor ) ||
+ /**
+ * If the environment does not support retrieving property descriptors
+ * (ES5), then the following will be true:
+ * - get/set will always be undefined
+ * - writable, enumerable and configurable will always be true
+ * - value will be the value of the requested property on the given object
+ *
+ * @param {!Object} obj object to check property on
+ * @param {string} prop property to retrieve descriptor for
+ *
+ * @return {Object|undefined} descriptor for requested property, if found
+ */
+ function( obj, prop )
+ {
+ if ( !Object.prototype.hasOwnProperty.call( obj, prop ) )
+ {
+ return undefined;
+ }
+
+ // fallback response
+ return {
+ get: undefined,
+ set: undefined,
+
+ writable: true,
+ enumerable: true,
+ configurable: true,
+
+ value: obj[ prop ]
+ };
+ };
+
+
+/**
+ * Returns prototype of object, or undefined if unsupported
+ */
+exports.getPrototypeOf = Object.getPrototypeOf || function()
+{
+ return undefined;
+};
+
+
+/**
+ * Travels down the prototype chain of the given object in search of the
+ * requested property and returns its descriptor
+ *
+ * This operates as Object.getOwnPropertyDescriptor(), except that it traverses
+ * the prototype chain. For environments that do not support __proto__, it will
+ * not traverse the prototype chain and essentially serve as an alias for
+ * getOwnPropertyDescriptor().
+ *
+ * This method has the option to ignore the base prototype. This is useful to,
+ * for example, not catch properties like Object.prototype.toString() when
+ * searching for 'toString' on an object.
+ *
+ * @param {Object} obj object to check property on
+ * @param {string} prop property to retrieve descriptor for
+ * @param {boolean} nobase whether to ignore the base prototype
+ *
+ * @return {Object} descriptor for requested property or undefined if not found
+ */
+exports.getPropertyDescriptor = function( obj, prop, nobase )
+{
+ // false by default
+ nobase = !!nobase;
+
+ // note that this uses util's function, not Object's
+ var desc = exports.getOwnPropertyDescriptor( obj, prop ),
+ next = exports.getPrototypeOf( obj );
+
+ // if we didn't find a descriptor and a prototype is available, recurse down
+ // the prototype chain, ensuring that the next prototype has a prototype if
+ // the base is to be excluded
+ if ( !desc && next && ( !nobase || exports.getPrototypeOf( next ) ) )
+ {
+ return exports.getPropertyDescriptor( next, prop, nobase );
+ }
+
+ // return the descriptor or undefined if no prototype is available
+ return desc;
+};
+
+
+/**
+ * Indicates whether or not the getPropertyDescriptor method is capable of
+ * traversing the prototype chain
+ *
+ * @type {boolean}
+ */
+exports.defineSecureProp( exports.getPropertyDescriptor, 'canTraverse',
+ ( Object.getPrototypeOf ) ? true : false
+);
+
+
+/**
+ * Appropriately returns defineSecureProp implementation to avoid check on each
+ * invocation
+ *
+ * @return {function( Object, string, * )}
+ */
+function getDefineSecureProp()
+{
+ // falls back to simply defining a normal property
+ var fallback = function( obj, prop, value )
+ {
+ obj[ prop ] = value;
+ };
+
+ if ( !can_define_prop )
+ {
+ return fallback;
+ }
+ else
+ {
+ // uses ECMAScript 5's Object.defineProperty() method
+ return function( obj, prop, value )
+ {
+ try
+ {
+ Object.defineProperty( obj, prop,
+ {
+ value: value,
+
+ enumerable: false,
+ writable: false,
+ configurable: false
+ });
+ }
+ catch ( e )
+ {
+ // let's not have this happen again, as repeatedly throwing
+ // exceptions will do nothing but slow down the system
+ exports.definePropertyFallback( true );
+
+ // if there's an error (ehem, IE8), fall back
+ fallback( obj, prop, value );
+ }
+ };
+ }
+}
+
+} )( module['util'] = {}, '.' );
+/** warn **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * ease.js warning system
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+/**
+ * Active warning handler
+ * @type {?function( Warning )}
+ */
+var _handler = null;
+
+/**
+ * Console to use for logging
+ *
+ * This reference allows an alternative console to be used. Must contain warn()
+ * or log() methods.
+ *
+ * @type {Object}
+ */
+var _console = ( typeof console !== 'undefined' ) ? console : undefined;
+
+
+/**
+ * Permits wrapping an exception as a warning
+ *
+ * Warnings are handled differently by the system, depending on the warning
+ * level that has been set.
+ *
+ * @param {Error} e exception (error) to wrap
+ *
+ * @return {Warning} new warning instance
+ *
+ * @constructor
+ */
+var Warning = exports.Warning = function( e )
+{
+ // allow instantiation without use of 'new' keyword
+ if ( !( this instanceof Warning ) )
+ {
+ return new Warning( e );
+ }
+
+ // ensure we're wrapping an exception
+ if ( !( e instanceof Error ) )
+ {
+ throw TypeError( "Must provide exception to wrap" );
+ }
+
+ Error.prototype.constructor.call( this, e.message );
+
+ // copy over the message for convenience
+ this.message = e.message;
+ this.name = 'Warning';
+ this._error = e;
+
+ this.stack = e.stack &&
+ e.stack.replace( /^.*?\n+/,
+ this.name + ': ' + this.message + "\n"
+ );
+};
+
+// ensures the closest compatibility...just be careful not to modify Warning's
+// prototype
+Warning.prototype = Error();
+Warning.prototype.constructor = Warning;
+Warning.prototype.name = 'Warning';
+
+
+/**
+ * Return the error wrapped by the warning
+ *
+ * @return {Error} wrapped error
+ */
+Warning.prototype.getError = function()
+{
+ return this._error;
+};
+
+
+/**
+ * Core warning handlers
+ * @type {Object}
+ */
+exports.handlers = {
+ /**
+ * Logs message to console
+ *
+ * Will attempt to log using console.warn(), falling back to console.log()
+ * if necessary and aborting entirely if neither is available.
+ *
+ * This is useful as a default option to bring problems to the developer's
+ * attention without affecting the control flow of the software.
+ *
+ * @param {Warning} warning to log
+ *
+ * @return {undefined}
+ */
+ log: function( warning )
+ {
+ var dest;
+
+ _console && ( dest = _console.warn || _console.log ) &&
+ dest.call( _console, ( 'Warning: ' + warning.message ) );
+ },
+
+
+ /**
+ * Throws the error associated with the warning
+ *
+ * This handler is useful for development and will ensure that problems are
+ * brought to the attention of the developer.
+ *
+ * @param {Warning} warning to log
+ *
+ * @return {undefined}
+ */
+ throwError: function( warning )
+ {
+ throw warning.getError();
+ },
+
+
+ /**
+ * Ignores warnings
+ *
+ * This is useful in a production environment where (a) warnings will affect
+ * the reputation of the software or (b) warnings may provide too much
+ * insight into the software. If using this option, you should always
+ * develop in a separate environment so that the system may bring warnings
+ * to your attention.
+ *
+ * @param {Warning} warning to log
+ *
+ * @return {undefined}
+ */
+ dismiss: function( warning )
+ {
+ // do nothing
+ }
+};
+
+
+/**
+ * Sets the active warning handler
+ *
+ * You may use any of the predefined warning handlers or pass your own function.
+ *
+ * @param {function( Warning )} handler warning handler
+ *
+ * @return {undefined}
+ */
+exports.setHandler = function( handler )
+{
+ _handler = handler;
+};
+
+
+/**
+ * Handles a warning using the active warning handler
+ *
+ * @param {Warning} warning warning to handle
+ *
+ * @return {undefined}
+ */
+exports.handle = function( warning )
+{
+ _handler( warning );
+}
+
+
+/**
+ * Sets active console
+ *
+ * @param {Object} console containing warn() or log() method
+ *
+ * @return {undefined}
+ */
+exports.setConsole = function( console )
+{
+ _console = console;
+};
+
+
+// set the default handler
+_handler = exports.handlers.log;
+
+} )( module['warn'] = {}, '.' );
+/** ClassBuilder **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Handles building of classes
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ *
+ * TODO: This module is currently being tested /indirectly/ by the class tests.
+ * This is because of a refactoring. All of this logic used to be part of
+ * the class module. Test this module directly, but keep the existing
+ * class tests in tact for a higher-level test.
+ */
+
+var util = require( __dirname + '/util' ),
+ warn = require( __dirname + '/warn' ),
+ Warning = warn.Warning,
+
+ /**
+ * IE contains a nasty enumeration "bug" (poor implementation) that makes
+ * toString unenumerable. This means that, if you do obj.toString = foo,
+ * toString will NOT show up in `for` or hasOwnProperty(). This is a problem.
+ *
+ * This test will determine if this poor implementation exists.
+ */
+ enum_bug = (
+ Object.prototype.propertyIsEnumerable.call(
+ { toString: function() {} },
+ 'toString'
+ ) === false
+ )
+ ? true
+ : false,
+
+ /**
+ * Hash of reserved members
+ *
+ * These methods cannot be defined in the class. They are for internal use
+ * only. We must check both properties and methods to ensure that neither is
+ * defined.
+ *
+ * @type {Object.}
+ */
+ reserved_members = {
+ '__initProps': true,
+ 'constructor': true
+ },
+
+ /**
+ * Hash of methods that must be public
+ *
+ * Notice that this is a list of /methods/, not members, because this check
+ * is performed only for methods. This is for performance reasons. We do not
+ * have a situation where we will want to check for properties as well.
+ *
+ * @type {Object.}
+ */
+ public_methods = {
+ '__construct': true,
+ 'toString': true,
+ '__toString': true
+ };
+
+
+/**
+ * Initializes class builder with given member builder
+ *
+ * The 'new' keyword is not required when instantiating this constructor.
+ *
+ * @param {Object} member_builder member builder
+ *
+ * @param {VisibilityObjectFactory} visibility_factory visibility object
+ * generator
+ *
+ * @constructor
+ */
+module.exports = exports =
+function ClassBuilder( member_builder, visibility_factory )
+{
+ // allow ommitting the 'new' keyword
+ if ( !( this instanceof exports ) )
+ {
+ // module.exports for Closure Compiler
+ return new module.exports( member_builder, visibility_factory );
+ }
+
+ /**
+ * Used for building class members
+ * @type {Object}
+ */
+ this._memberBuilder = member_builder;
+
+ /**
+ * Generates visibility object
+ * @type {VisibilityObjectFactory}
+ */
+ this._visFactory = visibility_factory;
+
+
+ /**
+ * Class id counter, to be increment on each new definition
+ * @type {number}
+ */
+ this._classId = 0;
+
+ /**
+ * Instance id counter, to be incremented on each new instance
+ * @type {number}
+ */
+ this._instanceId = 0;
+
+ /**
+ * Set to TRUE when class is in the process of being extended to ensure that
+ * a constructor can be instantiated (to use as the prototype) without
+ * invoking the class construction logic
+ *
+ * @type {boolean}
+ */
+ this._extending = false;
+
+ /**
+ * A flag to let the system know that we are currently attempting to access
+ * a static property from within a method. This means that the caller should
+ * be given access to additional levels of visibility.
+ *
+ * @type {boolean}
+ */
+ this._spropInternal = false;
+};
+
+
+/**
+ * Default class implementation
+ *
+ * @return undefined
+ */
+exports.ClassBase = function Class() {};
+
+
+/**
+ * Default static property method
+ *
+ * This simply returns undefined, signifying that the property was not found.
+ *
+ * @param {string} prop requested property
+ *
+ * @return {undefined}
+ */
+exports.ClassBase.$ = function( prop, val )
+{
+ if ( val !== undefined )
+ {
+ throw ReferenceError(
+ "Cannot set value of undeclared static property '" + prop + "'"
+ );
+ }
+
+ return undefined;
+};
+
+
+/**
+ * Returns a hash of the reserved members
+ *
+ * The returned object is a copy of the original. It cannot be used to modify
+ * the internal list of reserved members.
+ *
+ * @return {Object.} reserved members
+ */
+exports.getReservedMembers = function()
+{
+ // return a copy of the reserved members
+ return util.clone( reserved_members, true );
+};
+
+
+/**
+ * Returns a hash of the forced-public methods
+ *
+ * The returned object is a copy of the original. It cannot be used to modify
+ * the internal list of reserved members.
+ *
+ * @return {Object.} forced-public methods
+ */
+exports.getForcedPublicMethods = function()
+{
+ return util.clone( public_methods, true );
+};
+
+
+/**
+ * Returns reference to metadata for the requested class
+ *
+ * Since a reference is returned (rather than a copy), the returned object can
+ * be modified to alter the metadata.
+ *
+ * @param {Function|Object} cls class from which to retrieve metadata
+ *
+ * @return {__class_meta}
+ */
+exports.getMeta = function( cls )
+{
+ return cls.___$$meta$$ || {};
+}
+
+
+/**
+ * Determines if the class is an instance of the given type
+ *
+ * The given type can be a class, interface, trait or any other type of object.
+ * It may be used in place of the 'instanceof' operator and contains additional
+ * enhancements that the operator is unable to provide due to prototypal
+ * restrictions.
+ *
+ * @param {Object} type expected type
+ * @param {Object} instance instance to check
+ *
+ * @return {boolean} true if instance is an instance of type, otherwise false
+ */
+exports.isInstanceOf = function( type, instance )
+{
+ var meta, implemented, i;
+
+ if ( !( type && instance ) )
+ {
+ return false;
+ }
+
+ try
+ {
+ // check prototype chain (will throw an error if type is not a
+ // constructor (function)
+ if ( instance instanceof type )
+ {
+ return true;
+ }
+ }
+ catch ( e ) {}
+
+ // if no metadata is available, then our remaining checks cannot be
+ // performed
+ if ( !instance.__cid || !( meta = exports.getMeta( instance ) ) )
+ {
+ return false;
+ }
+
+ implemented = meta.implemented;
+ i = implemented.length;
+
+ // check implemented interfaces
+ while ( i-- )
+ {
+ if ( implemented[ i ] === type )
+ {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+
+/**
+ * Mimics class inheritance
+ *
+ * This method will mimic inheritance by setting up the prototype with the
+ * provided base class (or, by default, Class) and copying the additional
+ * properties atop of it.
+ *
+ * The class to inherit from (the first argument) is optional. If omitted, the
+ * first argument will be considered to be the properties list.
+ *
+ * @param {Function|Object} _ parent or definition object
+ * @param {Object=} __ definition object if parent was provided
+ *
+ * @return {Function} extended class
+ */
+exports.prototype.build = function extend( _, __ )
+{
+ // ensure we'll be permitted to instantiate abstract classes for the base
+ this._extending = true;
+
+ var args = Array.prototype.slice.call( arguments ),
+ props = args.pop() || {},
+ base = args.pop() || exports.ClassBase,
+ prototype = this._getBase( base ),
+ cname = '',
+
+ prop_init = this._memberBuilder.initMembers(),
+ members = this._memberBuilder.initMembers( prototype ),
+ static_members = {
+ methods: this._memberBuilder.initMembers(),
+ props: this._memberBuilder.initMembers()
+ },
+
+ abstract_methods =
+ util.clone( exports.getMeta( base ).abstractMethods )
+ || { __length: 0 }
+ ;
+
+ // prevent extending final classes
+ if ( base.___$$final$$ === true )
+ {
+ throw Error(
+ "Cannot extend final class " +
+ ( base.___$$meta$$.name || '(anonymous)' )
+ );
+ }
+
+ // grab the name, if one was provided
+ if ( cname = props.__name )
+ {
+ // we no longer need it
+ delete props.__name;
+ }
+
+ // IE has problems with toString()
+ if ( enum_bug )
+ {
+ if ( props.toString !== Object.prototype.toString )
+ {
+ props.__toString = props.toString;
+ }
+ }
+
+ // increment class identifier
+ this._classId++;
+
+ // build the various class components (xxx: this is temporary; needs
+ // refactoring)
+ try
+ {
+ this.buildMembers( props,
+ this._classId,
+ base,
+ prop_init,
+ abstract_methods,
+ members,
+ static_members,
+ function( inst )
+ {
+ return new_class.___$$svis$$;
+ }
+ );
+ }
+ catch ( e )
+ {
+ // intercept warnings /only/
+ if ( e instanceof Warning )
+ {
+ warn.handle( e );
+ }
+ else
+ {
+ throw e;
+ }
+ }
+
+ // reference to the parent prototype (for more experienced users)
+ prototype.___$$parent$$ = base.prototype;
+
+ // set up the new class
+ var new_class = this.createCtor( cname, abstract_methods, members );
+
+ // closure to hold static initialization to be used later by subtypes
+ initStaticVisibilityObj( new_class );
+ var staticInit = function( ctor, inheriting )
+ {
+ attachStatic( ctor, static_members, base, inheriting );
+ }
+ staticInit( new_class, false );
+
+ this._attachPropInit(
+ prototype, prop_init, members, new_class, this._classId
+ );
+
+ new_class.prototype = prototype;
+ new_class.prototype.constructor = new_class;
+ new_class.___$$props$$ = prop_init;
+ new_class.___$$methods$$ = members;
+ new_class.___$$sinit$$ = staticInit;
+
+ attachFlags( new_class, props );
+
+ validateAbstract( new_class, cname, abstract_methods );
+
+ // We reduce the overall cost of this definition by defining it on the
+ // prototype rather than during instantiation. While this does increase the
+ // amount of time it takes to access the property through the prototype
+ // chain, it takes much more time to define the property in this manner.
+ // Therefore, we can save a substantial amount of time by defining it on the
+ // prototype rather than on each new instance via __initProps().
+ util.defineSecureProp( prototype, '__self', new_class.___$$svis$$ );
+
+ // create internal metadata for the new class
+ var meta = createMeta( new_class, base );
+ meta.abstractMethods = abstract_methods;
+ meta.name = cname;
+
+ attachAbstract( new_class, abstract_methods );
+ attachId( new_class, this._classId );
+
+ // we're done with the extension process
+ this._extending = false;
+
+ return new_class;
+};
+
+
+exports.prototype._getBase = function( base )
+{
+ var type = ( typeof base );
+
+ switch ( type )
+ {
+ // constructor (we could also check to ensure that the return value of
+ // the constructor is an object, but that is not our concern)
+ case 'function':
+ return new base();
+
+ // we can use objects as the prototype directly
+ case 'object':
+ return base;
+ }
+
+ // scalars
+ throw TypeError( 'Must extend from Class, constructor or object' );
+};
+
+
+exports.prototype.buildMembers = function buildMembers(
+ props, class_id, base, prop_init, abstract_methods, members,
+ static_members, staticInstLookup
+)
+{
+ var hasOwn = Array.prototype.hasOwnProperty,
+ defs = {},
+
+ smethods = static_members.methods,
+ sprops = static_members.props,
+
+ _self = this
+ ;
+
+ util.propParse( props, {
+ each: function( name, value, keywords )
+ {
+ // disallow use of our internal __initProps() method
+ if ( reserved_members[ name ] === true )
+ {
+ throw Error( name + " is reserved" );
+ }
+
+ // if a member was defined multiple times in the same class
+ // declaration, throw an error
+ if ( hasOwn.call( defs, name ) )
+ {
+ throw Error(
+ "Cannot redefine method '" + name + "' in same declaration"
+ );
+ }
+
+ // keep track of the definitions (only during class declaration)
+ // to catch duplicates
+ defs[ name ] = 1;
+ },
+
+ property: function( name, value, keywords )
+ {
+ var dest = ( keywordStatic( keywords ) ) ? sprops : prop_init;
+
+ // build a new property, passing in the other members to compare
+ // against for preventing nonsensical overrides
+ _self._memberBuilder.buildProp(
+ dest, null, name, value, keywords, base
+ );
+ },
+
+ getset: function( name, get, set, keywords )
+ {
+ var dest = ( keywordStatic( keywords ) ) ? smethods : members,
+ is_static = keywordStatic( keywords ),
+ instLookup = ( ( is_static )
+ ? staticInstLookup
+ : exports.getMethodInstance
+ );
+
+ _self._memberBuilder.buildGetterSetter(
+ dest, null, name, get, set, keywords, instLookup, class_id, base
+ );
+ },
+
+ method: function( name, func, is_abstract, keywords )
+ {
+ var is_static = keywordStatic( keywords ),
+ dest = ( is_static ) ? smethods : members,
+ instLookup = ( is_static )
+ ? staticInstLookup
+ : exports.getMethodInstance
+ ;
+
+ // constructor check
+ if ( public_methods[ name ] === true )
+ {
+ if ( keywords[ 'protected' ] || keywords[ 'private' ] )
+ {
+ throw TypeError(
+ name + " must be public"
+ );
+ }
+ }
+
+ _self._memberBuilder.buildMethod(
+ dest, null, name, func, keywords, instLookup,
+ class_id, base
+ );
+
+ if ( is_abstract )
+ {
+ abstract_methods[ name ] = true;
+ abstract_methods.__length++;
+ }
+ else if ( ( hasOwn.call( abstract_methods, name ) )
+ && ( is_abstract === false )
+ )
+ {
+ // if this was a concrete method, then it should no longer
+ // be marked as abstract
+ delete abstract_methods[ name ];
+ abstract_methods.__length--;
+ }
+ }
+ } );
+}
+
+
+/**
+ * Validates abstract class requirements
+ *
+ * @param {function()} ctor class
+ * @param {string} cname class name
+ * @param {{__length}} abstract_methods object containing abstract methods
+ *
+ * @return {undefined}
+ */
+function validateAbstract( ctor, cname, abstract_methods )
+{
+ if ( ctor.___$$abstract$$ )
+ {
+ if ( abstract_methods.__length === 0 )
+ {
+ throw TypeError(
+ "Class " + ( cname || "(anonymous)" ) + " was declared as " +
+ "abstract, but contains no abstract members"
+ );
+ }
+ }
+ else
+ {
+ if ( abstract_methods.__length > 0 )
+ {
+ throw TypeError(
+ "Class " + ( cname || "(anonymous)" ) + " contains abstract " +
+ "members and must therefore be declared abstract"
+ );
+ }
+ }
+}
+
+
+/**
+ * Creates the constructor for a new class
+ *
+ * This constructor will call the __constructor method for concrete classes
+ * and throw an exception for abstract classes (to prevent instantiation).
+ *
+ * @param {string} cname class name (may be empty)
+ * @param {Array.} abstract_methods list of abstract methods
+ * @param {Object} members class members
+ *
+ * @return {Function} constructor
+ */
+exports.prototype.createCtor = function( cname, abstract_methods, members )
+{
+ // concrete class
+ if ( abstract_methods.__length === 0 )
+ {
+ return this.createConcreteCtor( cname, members );
+ }
+ // abstract class
+ else
+ {
+ return this.createAbstractCtor( cname );
+ }
+}
+
+
+/**
+ * Creates the constructor for a new concrete class
+ *
+ * This constructor will call the __constructor method of the class, if
+ * available.
+ *
+ * @param {string} cname class name (may be empty)
+ * @param {Object} members class members
+ *
+ * @return {function()} constructor
+ */
+exports.prototype.createConcreteCtor = function( cname, members )
+{
+ var args = null,
+ _self = this;
+
+ /**
+ * Constructor function to be returned
+ *
+ * The name is set to ClassInstance because some debuggers (e.g. v8) will
+ * show the name of this function for constructor instances rather than
+ * invoking the toString() method
+ *
+ * @constructor
+ *
+ * Suppressing due to complaints for using __initProps
+ * @suppress {checkTypes}
+ */
+ function ClassInstance()
+ {
+ if ( !( this instanceof ClassInstance ) )
+ {
+ // store arguments to be passed to constructor and
+ // instantiate new object
+ args = arguments;
+ return new ClassInstance();
+ }
+
+ initInstance( this );
+ this.__initProps();
+
+ // If we're extending, we don't actually want to invoke any class
+ // construction logic. The above is sufficient to use this class in a
+ // prototype, so stop here.
+ if ( _self._extending )
+ {
+ return;
+ }
+
+ // generate and store unique instance id
+ attachInstanceId( this, ++_self._instanceId );
+
+ // call the constructor, if one was provided
+ if ( typeof this.__construct === 'function' )
+ {
+ // note that since 'this' refers to the new class (even
+ // subtypes), and since we're using apply with 'this', the
+ // constructor will be applied to subtypes without a problem
+ this.__construct.apply( this, ( args || arguments ) );
+ args = null;
+ }
+
+ // attach any instance properties/methods (done after
+ // constructor to ensure they are not overridden)
+ attachInstanceOf( this );
+
+ // Provide a more intuitive string representation of the class
+ // instance. If a toString() method was already supplied for us,
+ // use that one instead.
+ if ( !( Object.prototype.hasOwnProperty.call(
+ members[ 'public' ], 'toString'
+ ) ) )
+ {
+ // use __toString if available (see enum_bug), otherwise use
+ // our own defaults
+ this.toString = members[ 'public' ].__toString
+ || ( ( cname )
+ ? function()
+ {
+ return '#<' + cname + '>';
+ }
+ : function()
+ {
+ return '#';
+ }
+ )
+ ;
+ }
+
+ };
+
+ // provide a more intuitive string representation
+ ClassInstance.toString = ( cname )
+ ? function() { return cname; }
+ : function() { return '(Class)'; }
+ ;
+
+ return ClassInstance;
+}
+
+
+/**
+ * Creates the constructor for a new abstract class
+ *
+ * Calling this constructor will cause an exception to be thrown, as abstract
+ * classes cannot be instantiated.
+ *
+ * @param {string} cname class name (may be empty)
+ *
+ * @return {function()} constructor
+ */
+exports.prototype.createAbstractCtor = function( cname )
+{
+ var _self = this;
+
+ var __abstract_self = function()
+ {
+ if ( !_self._extending )
+ {
+ throw Error(
+ "Abstract class " + ( cname || '(anonymous)' ) +
+ " cannot be instantiated"
+ );
+ }
+ };
+
+ __abstract_self.toString = ( cname )
+ ? function()
+ {
+ return cname;
+ }
+ : function()
+ {
+ return '(AbstractClass)';
+ }
+ ;
+
+ return __abstract_self;
+}
+
+
+/**
+ * Attaches __initProps() method to the class prototype
+ *
+ * The __initProps() method will initialize class properties for that instance,
+ * ensuring that their data is not shared with other instances (this is not a
+ * problem with primitive data types).
+ *
+ * The method will also initialize any parent properties (recursive) to ensure
+ * that subtypes do not have a referencing issue, and subtype properties take
+ * precedence over those of the parent.
+ *
+ * @param {Object} prototype prototype to attach method to
+ * @param {Object} properties properties to initialize
+ *
+ * @param {{public: Object, protected: Object, private: Object}} members
+ *
+ * @param {function()} ctor class
+ * @param {number} cid class id
+ *
+ * @return {undefined}
+ */
+exports.prototype._attachPropInit = function(
+ prototype, properties, members, ctor, cid
+)
+{
+ var _self = this;
+
+ util.defineSecureProp( prototype, '__initProps', function( inherit )
+ {
+ // defaults to false
+ inherit = !!inherit;
+
+ var iid = this.__iid,
+ parent = prototype.___$$parent$$;
+
+ // first initialize the parent's properties, so that ours will overwrite
+ // them
+ var parent_init = parent && parent.__initProps;
+ if ( typeof parent_init === 'function' )
+ {
+ // call the parent prop_init, letting it know that it's been
+ // inherited so that it does not initialize private members or
+ // perform other unnecessary tasks
+ parent_init.call( this, true );
+ }
+
+ // this will return our property proxy, if supported by our environment,
+ // otherwise just a normal object with everything merged in
+ var inst_props = _self._visFactory.createPropProxy(
+ this, this.___$$vis$$, properties[ 'public' ]
+ );
+
+ // Copies all public and protected members into inst_props and stores
+ // private in a separate object, which adds inst_props to its prototype
+ // chain and is returned. This is stored in a property referenced by the
+ // class id, so that the private members can be swapped on each method
+ // request, depending on calling context.
+ var vis = this.___$$vis$$[ cid ] = _self._visFactory.setup(
+ inst_props, properties, members
+ );
+
+ // provide a means to access the actual instance (rather than the
+ // property/visibility object) internally (this will translate to
+ // this.__inst from within a method), but only if we're on our final
+ // object (not a parent)
+ if ( !inherit )
+ {
+ util.defineSecureProp( vis, '__inst', this );
+ }
+ });
+}
+
+
+/**
+ * Determines if the given keywords should result in a static member
+ *
+ * A member will be considered static if the static or const keywords are given.
+ *
+ * @param {Object} keywords keywords to scan
+ *
+ * @return {boolean} true if to be static, otherwise false
+ */
+function keywordStatic( keywords )
+{
+ return ( keywords[ 'static' ] || keywords[ 'const' ] )
+ ? true
+ : false
+ ;
+}
+
+
+/**
+ * Creates and populates the static visibility object
+ *
+ * @param {Function} ctor class
+ *
+ * @return {undefined}
+ */
+function initStaticVisibilityObj( ctor )
+{
+ var _self = this;
+
+ /**
+ * the object will simply be another layer in the prototype chain to
+ * prevent protected/private members from being mixed in with the public
+ *
+ * @constructor
+ */
+ var sobj = function() {};
+ sobj.prototype = ctor;
+
+ var sobji = new sobj();
+
+ // override __self on the instance's visibility object, giving internal
+ // methods access to the restricted static methods
+ ctor.___$$svis$$ = sobji;
+
+ // Override the class-level accessor method to allow the system to know we
+ // are within a method. An internal flag is necessary, rather than using an
+ // argument or binding, because those two options are exploitable. An
+ // internal flag cannot be modified by conventional means.
+ sobji.$ = function()
+ {
+ _self._spropInternal = true;
+ var val = ctor.$.apply( ctor, arguments );
+ _self._spropInternal = false;
+
+ return val;
+ };
+}
+
+
+/**
+ * Attaches static members to a constructor (class)
+ *
+ * Static methods will be assigned to the constructor itself. Properties, on the
+ * other hand, will be assigned to ctor.$. The reason for this is because JS
+ * engines pre-ES5 support no means of sharing references to primitives. Static
+ * properties of subtypes should share references to the static properties of
+ * their parents.
+ *
+ * @param {function()} ctor class
+ * @param {Object} members static members
+ * @param {function()} base base class inheriting from
+ * @param {boolean} inheriting true if inheriting static members,
+ * otherwise false (setting own static
+ * members)
+ *
+ * @return {undefined}
+ */
+function attachStatic( ctor, members, base, inheriting )
+{
+ var methods = members.methods,
+ props = members.props,
+ _self = this
+ ;
+
+ // "Inherit" the parent's static methods by running the parent's static
+ // initialization method. It is important that we do this before anything,
+ // because this will recursively inherit all members in order, permitting
+ // overrides.
+ var baseinit = base.___$$sinit$$;
+ if ( baseinit )
+ {
+ baseinit( ctor, true );
+ }
+
+ // initialize static property if not yet defined
+ if ( !inheriting )
+ {
+ ctor.___$$sprops$$ = props;
+
+ // provide a method to access static properties
+ util.defineSecureProp( ctor, '$', function( prop, val )
+ {
+ // we use hasOwnProperty to ensure that undefined values will not
+ // cause us to continue checking the parent, thereby potentially
+ // failing to set perfectly legal values
+ var has = Object.prototype.hasOwnProperty,
+ found = false,
+
+ // Determine if we were invoked in the context of a class. If
+ // so, use that. Otherwise, use ourself.
+ context = ( this.___$$sprops$$ ) ? this : ctor,
+
+ // We are in a subtype if the context does not match the
+ // constructor. This works because, when invoked for the first
+ // time, this method is not bound to the constructor. In such a
+ // case, we default the context to the constructor and pass that
+ // down the line to each recursive call. Therefore, recursive
+ // calls to subtypes will have a context mismatch.
+ in_subtype = ( context !== ctor )
+ ;
+
+ // Attempt to locate the property. First, we check public. If not
+ // available and we are internal (within a method), we can move on
+ // to check other levels of visibility. `found` will contain the
+ // visibility level the property was found in, or false.
+ found = has.call( props[ 'public' ], prop ) && 'public';
+ if ( !found && _self._spropInternal )
+ {
+ // Check for protected/private. We only check for private
+ // properties if we are not currently checking the properties of
+ // a subtype. This works because the context is passed to each
+ // recursive call.
+ found = has.call( props[ 'protected' ], prop ) && 'protected'
+ || !in_subtype
+ && has.call( props[ 'private' ], prop ) && 'private'
+ ;
+ }
+
+ // if we don't own the property, let the parent(s) handle it
+ if ( found === false )
+ {
+ // TODO: This check is simple, but quick. It may be worth
+ // setting a flag on the class during definition to specify if
+ // it's extending from a non-class base.
+ return ( base.__cid && base.$ || exports.ClassBase.$ ).apply(
+ context, arguments
+ );
+ }
+
+ var prop_item = props[ found ][ prop ];
+
+ // if a value was provided, this method should be treated as a
+ // setter rather than a getter (we *must* test using
+ // arguments.length to ensure that setting to undefined works)
+ if ( arguments.length > 1 )
+ {
+ // if const, disallow modification
+ if ( prop_item[ 1 ][ 'const' ] )
+ {
+ throw TypeError(
+ "Cannot modify constant property '" + prop + "'"
+ );
+ }
+
+ prop_item[ 0 ] = val;
+ return context;
+ }
+ else
+ {
+ // return the value
+ return prop_item[ 0 ];
+ }
+ } );
+ }
+
+ // copy over public static methods
+ util.copyTo( ctor, methods[ 'public' ], true );
+ util.copyTo( ctor.___$$svis$$, methods[ 'protected' ], true );
+
+ // private methods should not be inherited by subtypes
+ if ( !inheriting )
+ {
+ util.copyTo( ctor.___$$svis$$, methods[ 'private' ], true );
+ }
+}
+
+
+/**
+ * Initializes class metadata for the given class
+ *
+ * @param {Function} func class to initialize metadata for
+ * @param {Function} cparent class parent
+ *
+ * @return {undefined}
+ *
+ * Suppressed due to warnings for use of __cid
+ * @suppress {checkTypes}
+ */
+function createMeta( func, cparent )
+{
+ var id = func.__cid,
+ parent_meta = ( ( cparent.__cid )
+ ? exports.getMeta( cparent )
+ : undefined
+ );
+
+ // copy the parent prototype's metadata if it exists (inherit metadata)
+ if ( parent_meta )
+ {
+ func.___$$meta$$ = util.clone( parent_meta, true );
+ }
+ else
+ {
+ // create empty
+ func.___$$meta$$ = {
+ implemented: []
+ };
+ }
+
+ // store the metadata in the prototype as well (inconsiderable overhead;
+ // it's just a reference)
+ func.prototype.___$$meta$$ = func.___$$meta$$;
+
+ return func.___$$meta$$;
+}
+
+
+/**
+ * Attaches an instance identifier to a class instance
+ *
+ * @param {Object} instance class instance
+ * @param {number} iid instance id
+ *
+ * @return {undefined}
+ */
+function attachInstanceId( instance, iid )
+{
+ util.defineSecureProp( instance, '__iid', iid );
+}
+
+
+/**
+ * Initializes class instance
+ *
+ * This process will create the instance visibility object that will contain
+ * private and protected members. The class instance is part of the prototype
+ * chain. This will be passed to all methods when invoked, permitting them to
+ * access the private and protected members while keeping them encapsulated.
+ *
+ * For each instance, there is always a base. The base will contain a proxy to
+ * the public members on the instance itself. The base will also contain all
+ * protected members.
+ *
+ * Atop the base object is a private member object, with the base as its
+ * prototype. There exists a private member object for the instance itself and
+ * one for each supertype. This is stored by the class id (cid) as the key. This
+ * permits the private member object associated with the class of the method
+ * call to be bound to that method. For example, if a parent method is called,
+ * that call must be invoked in the context of the parent, so the private
+ * members of the parent must be made available.
+ *
+ * The resulting structure looks something like this:
+ * class_instance = { iid: { cid: {} } }
+ *
+ * @param {Object} instance instance to initialize
+ *
+ * @return {undefined}
+ */
+function initInstance( instance )
+{
+ /** @constructor */
+ var prot = function() {};
+ prot.prototype = instance;
+
+ // add the visibility objects to the data object for this class instance
+ instance.___$$vis$$ = new prot();
+}
+
+
+/**
+ * Attaches partially applied isInstanceOf() method to class instance
+ *
+ * @param {Object} instance class instance to attach method to
+ *
+ * @return {undefined}
+ */
+function attachInstanceOf( instance )
+{
+ var method = function( type )
+ {
+ return module.exports.isInstanceOf( type, instance );
+ };
+
+ // TODO: To improve performance (defineSecureProp can be costly), simply
+ // define a normal prop and freeze the class afterward. The class shouldn't
+ // have any mutable methods.
+ util.defineSecureProp( instance, 'isInstanceOf', method );
+ util.defineSecureProp( instance, 'isA', method );
+}
+
+
+/**
+ * Returns the instance object associated with the given method
+ *
+ * The instance object contains the protected members. This object can be passed
+ * as the context when calling a method in order to give that method access to
+ * those members.
+ *
+ * One level above the instance object on the prototype chain is the object
+ * containing the private members. This is swappable, depending on the class id
+ * associated with the provided method call. This allows methods that were not
+ * overridden by the subtype to continue to use the private members of the
+ * supertype.
+ *
+ * @param {function()} inst instance that the method is being called from
+ * @param {number} cid class id
+ *
+ * @return {Object|null} instance object if found, otherwise null
+ *
+ * @suppress {checkTypes}
+ */
+exports.getMethodInstance = function( inst, cid )
+{
+ var iid = inst.__iid,
+ data = inst.___$$vis$$;
+
+ return ( iid && data )
+ ? data[ cid ]
+ : null
+ ;
+}
+
+
+/**
+ * Attaches isAbstract() method to the class
+ *
+ * @param {Function} func function (class) to attach method to
+ * @param {Array} methods abstract method names
+ *
+ * @return {undefined}
+ */
+function attachAbstract( func, methods )
+{
+ var is_abstract = ( methods.__length > 0 ) ? true: false;
+
+ /**
+ * Returns whether the class contains abstract methods (and is therefore
+ * abstract)
+ *
+ * @return {boolean} true if class is abstract, otherwise false
+ */
+ util.defineSecureProp( func, 'isAbstract', function()
+ {
+ return is_abstract;
+ });
+}
+
+
+/**
+ * Attaches the unique id to the class and its prototype
+ *
+ * The unique identifier is used internally to match a class and its instances
+ * with the class metadata. Exposing the id breaks encapsulation to a degree,
+ * but is a lesser evil when compared to exposing all metadata.
+ *
+ * @param {function()} ctor constructor (class) to attach method to
+ * @param {number} id id to assign
+ *
+ * @return {undefined}
+ */
+function attachId( ctor, id )
+{
+ util.defineSecureProp( ctor, '__cid', id );
+ util.defineSecureProp( ctor.prototype, '__cid', id );
+}
+
+
+/**
+ * Sets class flags
+ *
+ * @param {Function} ctor class to flag
+ * @param {Object} props class properties
+ *
+ * @return {undefined}
+ */
+function attachFlags( ctor, props )
+{
+ ctor.___$$final$$ = !!( props.___$$final$$ );
+ ctor.___$$abstract$$ = !!( props.___$$abstract$$ );
+
+ // The properties are no longer needed. Set to undefined rather than delete
+ // (v8 performance)
+ props.___$$final$$ = props.___$$abstract$$ = undefined;
+}
+
+} )( module['ClassBuilder'] = {}, '.' );
+/** MethodWrapperFactory **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Builds method wrappers
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+
+/**
+ * Initializes factory to wrap methods
+ *
+ * @param {function(Function,Function,number)} factory function that will
+ * perform the actual
+ * wrapping
+ *
+ * @constructor
+ */
+module.exports = exports = function MethodWrapperFactory( factory )
+{
+ // permit omission of the 'new' keyword for instantiation
+ if ( !( this instanceof exports ) )
+ {
+ // module.exports for Closure Compiler
+ return new module.exports( factory );
+ }
+
+ this._factory = factory;
+};
+
+
+/**
+ * Wraps the provided method
+ *
+ * The returned function is determined by the factory function provided when the
+ * MethodWrapperFactory was instantiated.
+ *
+ * @param {function()} method method to wrap
+ * @param {function()} super_method super method, if overriding
+ * @param {number} cid class id that method is associated with
+ * @param {function()} getInst function to determine instance and return
+ * associated visibility object
+ * @param {string=} name name of method
+ * @param {Object=} keywords method keywords
+ */
+exports.prototype.wrapMethod = function(
+ method, super_method, cid, getInst, name, keywords
+)
+{
+ return this._factory( method, super_method, cid, getInst, name, keywords );
+};
+
+} )( module['MethodWrapperFactory'] = {}, '.' );
+/** MethodWrappers **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Default method wrapper functions
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+/**
+ * Method wrappers for standard (non-fallback)
+ * @type {Object}
+ */
+exports.standard = {
+ wrapOverride: function( method, super_method, cid, getInst )
+ {
+ return function()
+ {
+ var context = getInst( this, cid ) || this,
+ retval = undefined
+ ;
+
+ // the _super property will contain the parent method (we don't
+ // store the previous value for performance reasons and because,
+ // during conventional use, it's completely unnecessary)
+ context.__super = super_method;
+
+ retval = method.apply( context, arguments );
+
+ // prevent sneaky bastards from breaking encapsulation by stealing
+ // method references (we set to undefined rather than deleting it
+ // because deletion causes performance degradation within V8)
+ context.__super = undefined;
+
+ // if the value returned from the method was the context that we
+ // passed in, return the actual instance (to ensure we do not break
+ // encapsulation)
+ if ( retval === context )
+ {
+ return this;
+ }
+
+ return retval;
+ };
+ },
+
+
+ wrapNew: function( method, super_method, cid, getInst )
+ {
+ return function()
+ {
+ var context = getInst( this, cid ) || this,
+ retval = undefined
+ ;
+
+ // invoke the method
+ retval = method.apply( context, arguments );
+
+ // if the value returned from the method was the context that we
+ // passed in, return the actual instance (to ensure we do not break
+ // encapsulation)
+ if ( retval === context )
+ {
+ return this;
+ }
+
+ return retval;
+ };
+ },
+
+
+ wrapProxy: function( proxy_to, _, cid, getInst, name, keywords )
+ {
+ // it is important that we store only a boolean value as to whether or
+ // not this method is static *outside* of the returned closure, so as
+ // not to keep an unnecessary reference to the keywords object
+ var is_static = keywords && keywords[ 'static' ];
+
+ return function()
+ {
+ var context = getInst( this, cid ) || this,
+ retval = undefined,
+ dest = ( ( is_static )
+ ? context.$( proxy_to )
+ : context[ proxy_to ]
+ )
+ ;
+
+ // rather than allowing a cryptic error to be thrown, attempt to
+ // detect when the proxy call will fail and provide a useful error
+ // message
+ if ( !( ( dest !== null ) && ( typeof dest === 'object' )
+ && ( typeof dest[ name ] === 'function' )
+ ) )
+ {
+ throw TypeError(
+ "Unable to proxy " + name + "() call to '" + proxy_to +
+ "'; '" + proxy_to + "' is undefined or '" + name +
+ "' is not a function."
+ );
+ }
+
+ retval = dest[ name ].apply( dest, arguments );
+
+ // if the object we are proxying to returns itself, then instead
+ // return a reference to *ourself* (so as not to break encapsulation
+ // and to provide a more consistent and sensible API)
+ return ( retval === dest )
+ ? this
+ : retval;
+ };
+ }
+};
+
+} )( module['MethodWrappers'] = {}, '.' );
+/** MemberBuilder **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Handles building members (properties, methods)
+ *
+ * This prototype could have easily been refactored into a number of others
+ * (e.g. one for each type of member), but that refactoring has been deferred
+ * until necessary to ensure ease.js maintains a relatively small footprint.
+ * Ultimately, however, such a decision is a micro-optimization and shouldn't
+ * harm the design and maintainability of the software.
+ *
+ * TODO: Implementation is inconsistent between various members. For example,
+ * methods use ___$$keywords$$, whereas properties use [ val, keywords ]. Decide
+ * on a common format.
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+var util = require( __dirname + '/util' ),
+ Warning = require( __dirname + '/warn' ).Warning,
+ visibility = [ 'public', 'protected', 'private' ]
+;
+
+
+/**
+ * Responsible for building class members
+ *
+ * @param {Function} wrap_method method wrapper
+ * @param {Function} wrap_override method override wrapper
+ * @param {Function} wrap_proxy method proxy wrapper
+ * @param {MemberBuilderValidator} validate member validator
+ *
+ * @constructor
+ */
+module.exports = function MemberBuilder(
+ wrap_method, wrap_override, wrap_proxy, validate
+)
+{
+ // permit omitting 'new' keyword
+ if ( !( this instanceof module.exports ) )
+ {
+ return new module.exports(
+ wrap_method, wrap_override, wrap_proxy, validate
+ );
+ }
+
+ this._wrapMethod = wrap_method;
+ this._wrapOverride = wrap_override;
+ this._wrapProxy = wrap_proxy;
+
+ this._validate = validate;
+};
+
+
+// we're throwing everything into the prototype
+exports = module.exports.prototype;
+
+
+/**
+ * Initializes member object
+ *
+ * The member object contains members for each level of visibility (public,
+ * protected and private).
+ *
+ * @param {Object} mpublic default public members
+ * @param {Object} mprotected default protected members
+ * @param {Object} mprivate default private members
+ *
+ * @return {__visobj}
+ */
+exports.initMembers = function( mpublic, mprotected, mprivate )
+{
+ return {
+ 'public': mpublic || {},
+ 'protected': mprotected || {},
+ 'private': mprivate || {}
+ };
+};
+
+
+/**
+ * Copies a method to the appropriate member prototype, depending on
+ * visibility, and assigns necessary metadata from keywords
+ *
+ * @param {__visobj} members
+ * @param {!Object} meta metadata container
+ * @param {string} name property name
+ * @param {*} value property value
+ *
+ * @param {!Object.} keywords parsed keywords
+ *
+ * @param {Function} instCallback function to call in order to retrieve
+ * object to bind 'this' keyword to
+ *
+ * @param {number} cid class id
+ * @param {Object=} base optional base object to scan
+ *
+ * @return {undefined}
+ */
+exports.buildMethod = function(
+ members, meta, name, value, keywords, instCallback, cid, base
+)
+{
+ // TODO: We can improve performance by not scanning each one individually
+ // every time this method is called
+ var prev_data = scanMembers( members, name, base ),
+ prev = ( prev_data ) ? prev_data.member : null,
+ prev_keywords = ( prev && prev.___$$keywords$$ ),
+ dest = getMemberVisibility( members, keywords, name );
+ ;
+
+ // ensure that the declaration is valid (keywords make sense, argument
+ // length, etc)
+ this._validate.validateMethod(
+ name, value, keywords, prev_data, prev_keywords
+ );
+
+ // we might be overriding an existing method
+ if ( keywords[ 'proxy' ] )
+ {
+ // TODO: Note that this is not compatible with method hiding, due to its
+ // positioning (see hideMethod() below); address once method hiding is
+ // implemented (the validators currently handle everything else)
+ dest[ name ] = this._createProxy(
+ value, instCallback, cid, name, keywords
+ );
+ }
+ else if ( prev )
+ {
+
+ if ( keywords[ 'override' ] || prev_keywords[ 'abstract' ] )
+ {
+ // override the method
+ dest[ name ] = this._overrideMethod(
+ prev, value, instCallback, cid
+ );
+ }
+ else
+ {
+ // by default, perform method hiding, even if the keyword was not
+ // provided (the keyword simply suppresses the warning)
+ dest[ name ] = hideMethod( prev, value, instCallback, cid );
+ }
+
+ }
+ else if ( keywords[ 'abstract' ] )
+ {
+ // we do not want to wrap abstract methods, since they are not callable
+ dest[ name ] = value;
+ }
+ else
+ {
+ // we are not overriding the method, so simply copy it over, wrapping it
+ // to ensure privileged calls will work properly
+ dest[ name ] = this._overrideMethod( null, value, instCallback, cid );
+ }
+
+ // store keywords for later reference (needed for pre-ES5 fallback)
+ dest[ name ].___$$keywords$$ = keywords;
+};
+
+
+/**
+ * Copies a property to the appropriate member prototype, depending on
+ * visibility, and assigns necessary metadata from keywords
+ *
+ * @param {__visobj} members
+ * @param {!Object} meta metadata container
+ * @param {string} name property name
+ * @param {*} value property value
+ *
+ * @param {!Object.} keywords parsed keywords
+ *
+ * @param {Object=} base optional base object to scan
+ *
+ * @return {undefined}
+ */
+exports.buildProp = function( members, meta, name, value, keywords, base )
+{
+ // TODO: We can improve performance by not scanning each one individually
+ // every time this method is called
+ var prev_data = scanMembers( members, name, base ),
+ prev = ( prev_data ) ? prev_data.member : null,
+ prev_keywords = ( prev ) ? prev[ 1 ] : null;
+
+ this._validate.validateProperty(
+ name, value, keywords, prev_data, prev_keywords
+ );
+
+ getMemberVisibility( members, keywords, name )[ name ] =
+ [ value, keywords ];
+};
+
+
+/**
+ * Copies a getter/setter to the appropriate member prototype, depending on
+ * visibility, and assigns necessary metadata from keywords
+ *
+ * TODO: This should essentially mirror buildMethod with regards to overrides,
+ * proxies, etc.
+ *
+ * @param {!__visobj} members
+ * @param {!Object} meta metadata container
+ * @param {string} name getter name
+ * @param {*} get getter value
+ * @param {*} set setter value
+ *
+ * @param {!Object.} keywords parsed keywords
+ *
+ * @param {Function} instCallback function to call in order to retrieve
+ * object to bind 'this' keyword to
+ *
+ * @param {number} cid class id
+ * @param {Object=} base optional base object to scan
+ *
+ * @return {undefined}
+ *
+ * Closure Compiler is improperly throwing warnings on Object.defineProperty():
+ * @suppress {checkTypes}
+ */
+exports.buildGetterSetter = function(
+ members, meta, name, get, set, keywords, instCallback, cid, base
+)
+{
+ var prev_data = scanMembers( members, name, base ),
+ prev_keywords = ( ( prev_data && prev_data.get )
+ ? prev_data.get.___$$keywords$$
+ : null
+ )
+ ;
+
+ this._validate.validateGetterSetter(
+ name, {}, keywords, prev_data, prev_keywords
+ );
+
+ if ( get )
+ {
+ get = this._overrideMethod( null, get, instCallback, cid );
+
+ // ensure we store the keywords *after* the override, otherwise they
+ // will be assigned to the wrapped function (the getter)
+ get.___$$keywords$$ = keywords;
+ }
+
+ Object.defineProperty(
+ getMemberVisibility( members, keywords, name ),
+ name,
+ {
+ get: get,
+ set: ( set )
+ ? this._overrideMethod( null, set, instCallback, cid )
+ : set,
+
+ enumerable: true,
+ configurable: false
+ }
+ );
+};
+
+
+/**
+ * Returns member prototype to use for the requested visibility
+ *
+ * Will throw an exception if multiple access modifiers were used.
+ *
+ * @param {__visobj} members
+ *
+ * @param {!Object.} keywords parsed keywords
+ * @param {string} name member name
+ *
+ * @return {Object} reference to visibility of members argument to use
+ */
+function getMemberVisibility( members, keywords, name )
+{
+ var viserr = function()
+ {
+ throw TypeError(
+ "Only one access modifier may be used for definition of '" +
+ name + "'"
+ );
+ }
+
+ // there's cleaner ways of doing this, but consider it loop unrolling for
+ // performance
+ if ( keywords[ 'private' ] )
+ {
+ ( keywords[ 'public' ] || keywords[ 'protected' ] ) && viserr();
+ return members[ 'private' ];
+ }
+ else if ( keywords[ 'protected' ] )
+ {
+ ( keywords[ 'public' ] || keywords[ 'private' ] ) && viserr();
+ return members[ 'protected' ];
+ }
+ else
+ {
+ // public keyword is the default, so explicitly specifying it is only
+ // for clarity
+ ( keywords[ 'private' ] || keywords[ 'protected' ] ) && viserr();
+ return members[ 'public' ];
+ }
+}
+
+
+/**
+ * Scan each level of visibility for the requested member
+ *
+ * @param {__visobj} members
+ *
+ * @param {string} name member to locate
+ * @param {Object=} base optional base object to scan
+ *
+ * @return {{get,set,member}|null}
+ */
+function scanMembers( members, name, base )
+{
+ var i = visibility.length,
+ member = null;
+
+ // locate requested member by scanning each level of visibility
+ while ( i-- )
+ {
+ var visobj = members[ visibility[ i ] ];
+
+ // In order to support getters/setters, we must go off of the
+ // descriptor. We must also ignore base properties (last argument), such
+ // as Object.prototype.toString(). However, we must still traverse the
+ // prototype chain.
+ if ( member = util.getPropertyDescriptor( visobj, name, true ) )
+ {
+ return {
+ get: member.get,
+ set: member.set,
+ member: member.value
+ };
+ }
+ }
+
+ // if a second comparison object was given, try again using it instead of
+ // the original members object
+ if ( base !== undefined )
+ {
+ var base_methods = base.___$$methods$$,
+ base_props = base.___$$props$$;
+
+ // scan the base's methods and properties, if they are available
+ return ( base_methods && scanMembers( base_methods, name ) )
+ || ( base_props && scanMembers( base_props, name ) )
+ || null
+ ;
+ }
+
+ // nothing was found
+ return null;
+}
+
+
+/**
+ * Hide a method with a "new" method
+ */
+function hideMethod( super_method, new_method, instCallback, cid )
+{
+ // TODO: This function is currently unimplemented. It exists at present to
+ // provide a placeholder and ensure that the override keyword is required to
+ // override a parent method.
+ //
+ // We should never get to this point if the default validation rule set is
+ // used to prevent omission of the 'override' keyword.
+ throw Error(
+ 'Method hiding not yet implemented (we should never get here; bug).'
+ );
+}
+
+
+/**
+ * Create a method that proxies to the method of another object
+ *
+ * @param {string} proxy_to name of property (of instance) to proxy to
+ *
+ * @param {Function} instCallback function to call in order to retrieve
+ * object to bind 'this' keyword to
+ *
+ * @param {number} cid class id
+ * @param {string} mname name of method to invoke on destination object
+ * @param {Object} keywords method keywords
+ *
+ * @return {Function} proxy method
+ */
+exports._createProxy = function( proxy_to, instCallback, cid, mname, keywords )
+{
+ return this._wrapProxy.wrapMethod(
+ proxy_to, null, cid, instCallback, mname, keywords
+ );
+};
+
+
+/**
+ * Generates a method override function
+ *
+ * The override function simply wraps the method so that its invocation will
+ * pass a __super property. This property may be used to invoke the overridden
+ * method.
+ *
+ * @param {function()} super_method method to override
+ * @param {function()} new_method method to override with
+ *
+ * @param {Function} instCallback function to call in order to retrieve
+ * object to bind 'this' keyword to
+ *
+ * @param {number} cid class id
+ *
+ * @return {function()} override method
+ */
+exports._overrideMethod = function(
+ super_method, new_method, instCallback, cid
+)
+{
+ instCallback = instCallback || function() {};
+
+ // return a function that permits referencing the super method via the
+ // __super property
+ var override = null;
+
+ // are we overriding?
+ override = (
+ ( super_method )
+ ? this._wrapOverride
+ : this._wrapMethod
+ ).wrapMethod( new_method, super_method, cid, instCallback );
+
+ // This is a trick to work around the fact that we cannot set the length
+ // property of a function. Instead, we define our own property - __length.
+ // This will store the expected number of arguments from the super method.
+ // This way, when a method is being overridden, we can check to ensure its
+ // compatibility with its super method.
+ util.defineSecureProp( override,
+ '__length',
+ ( new_method.__length || new_method.length )
+ );
+
+ return override;
+}
+
+
+/**
+ * Return the visibility level as a numeric value, where 0 is public and 2 is
+ * private
+ *
+ * @param {Object} keywords keywords to scan for visibility level
+ *
+ * @return {number} visibility level as a numeric value
+ */
+exports._getVisibilityValue = function( keywords )
+{
+ if ( keywords[ 'protected' ] )
+ {
+ return 1;
+ }
+ else if ( keywords[ 'private' ] )
+ {
+ return 2;
+ }
+ else
+ {
+ // default is public
+ return 0;
+ }
+}
+
+} )( module['MemberBuilder'] = {}, '.' );
+/** MemberBuilderValidator **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Validation rules for members
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+
+module.exports = exports = function MemberBuilderValidator( warn_handler )
+{
+ // permit omitting 'new' keyword
+ if ( !( this instanceof module.exports ) )
+ {
+ return new module.exports( warn_handler );
+ }
+
+ this._warningHandler = warn_handler || function() {};
+};
+
+
+/**
+ * Validates a method declaration, ensuring that keywords are valid, overrides
+ * make sense, etc.
+ *
+ * Throws exception on validation failure
+ *
+ * @param {string} name method name
+ * @param {*} value method value
+ *
+ * @param {Object.} keywords parsed keywords
+ *
+ * @param {Object} prev_data data of member being overridden
+ * @param {Object} prev_keywords keywords of member being overridden
+ *
+ * @return {undefined}
+ */
+exports.prototype.validateMethod = function(
+ name, value, keywords, prev_data, prev_keywords
+)
+{
+ var prev = ( prev_data ) ? prev_data.member : null;
+
+ if ( keywords[ 'abstract' ] )
+ {
+ // do not permit private abstract methods (doesn't make sense, since
+ // they cannot be inherited/overridden)
+ if ( keywords[ 'private' ] )
+ {
+ throw TypeError(
+ "Method '" + name + "' cannot be both private and abstract"
+ );
+ }
+ }
+
+ // const doesn't make sense for methods; they're always immutable
+ if ( keywords[ 'const' ] )
+ {
+ throw TypeError(
+ "Cannot declare method '" + name + "' as constant; keyword is " +
+ "redundant"
+ );
+ }
+
+ // virtual static does not make sense, as static methods cannot be
+ // overridden
+ if ( keywords[ 'virtual' ] && ( keywords[ 'static' ] ) )
+ {
+ throw TypeError(
+ "Cannot declare static method '" + name + "' as virtual"
+ );
+ }
+
+ // do not allow overriding getters/setters
+ if ( prev_data && ( prev_data.get || prev_data.set ) )
+ {
+ throw TypeError(
+ "Cannot override getter/setter '" + name + "' with method"
+ );
+ }
+
+ if ( keywords[ 'proxy' ] )
+ {
+ // proxies are expected to provide the name of the destination object
+ if ( typeof value !== 'string' )
+ {
+ throw TypeError(
+ "Cannot declare proxy method '" + name + "'; string value " +
+ "expected"
+ );
+ }
+ else if ( keywords[ 'abstract' ] )
+ {
+ // proxies are always concrete
+ throw TypeError(
+ "Proxy method '" + name + "' cannot be abstract"
+ );
+ }
+ }
+
+ // search for any previous instances of this member
+ if ( prev )
+ {
+ // perform this check first, as it will make more sense than those that
+ // follow, should this condition be satisfied
+ if ( prev_keywords[ 'private' ] )
+ {
+ throw TypeError(
+ "Private member name '" + name + "' conflicts with supertype"
+ );
+ }
+
+ // disallow overriding properties with methods
+ if ( !( typeof prev === 'function' ) )
+ {
+ throw TypeError(
+ "Cannot override property '" + name + "' with method"
+ );
+ }
+
+ // disallow overriding non-virtual methods
+ if ( keywords[ 'override' ] && !( prev_keywords[ 'virtual' ] ) )
+ {
+ throw TypeError(
+ "Cannot override non-virtual method '" + name + "'"
+ );
+ }
+
+ // do not allow overriding concrete methods with abstract
+ if ( keywords[ 'abstract' ] && !( prev_keywords[ 'abstract' ] ) )
+ {
+ throw TypeError(
+ "Cannot override concrete method '" + name + "' with " +
+ "abstract method"
+ );
+ }
+
+ // ensure parameter list is at least the length of its supertype
+ if ( ( value.__length || value.length )
+ < ( prev.__length || prev.length )
+ )
+ {
+ throw TypeError(
+ "Declaration of method '" + name + "' must be compatible " +
+ "with that of its supertype"
+ );
+ }
+
+ // do not permit visibility deescalation
+ if ( this._getVisibilityValue( prev_keywords ) <
+ this._getVisibilityValue( keywords )
+ )
+ {
+ throw TypeError(
+ "Cannot de-escalate visibility of method '" + name + "'"
+ );
+ }
+
+ // Disallow overriding method without override keyword (unless parent
+ // method is abstract). In the future, this will provide a warning to
+ // default to method hiding.
+ if ( !( keywords[ 'override' ] || prev_keywords[ 'abstract' ] ) )
+ {
+ throw TypeError(
+ "Attempting to override method '" + name +
+ "' without 'override' keyword"
+ );
+ }
+ }
+ else if ( keywords[ 'override' ] )
+ {
+ // using the override keyword without a super method may indicate a bug,
+ // but it shouldn't stop the class definition (it doesn't adversely
+ // affect the functionality of the class, unless of course the method
+ // attempts to reference a supertype)
+ this._warningHandler( Error(
+ "Method '" + name +
+ "' using 'override' keyword without super method"
+ ) );
+ }
+};
+
+
+/**
+ * Validates a property declaration, ensuring that keywords are valid, overrides
+ * make sense, etc.
+ *
+ * Throws exception on validation failure
+ *
+ * @param {string} name method name
+ * @param {*} value method value
+ *
+ * @param {Object.} keywords parsed keywords
+ *
+ * @param {Object} prev_data data of member being overridden
+ * @param {Object} prev_keywords keywords of member being overridden
+ *
+ * @return {undefined}
+ */
+exports.prototype.validateProperty = function(
+ name, value, keywords, prev_data, prev_keywords
+)
+{
+ var prev = ( prev_data ) ? prev_data.member : null;
+
+ // do not permit visibility de-escalation
+ if ( prev )
+ {
+ // perform this check first, as it will make more sense than those that
+ // follow, should this condition be satisfied
+ if ( prev_keywords[ 'private' ] )
+ {
+ throw TypeError(
+ "Private member name '" + name + "' conflicts with supertype"
+ );
+ }
+
+ // disallow overriding methods with properties
+ if ( typeof prev === 'function' )
+ {
+ throw new TypeError(
+ "Cannot override method '" + name + "' with property"
+ );
+ }
+
+ if ( this._getVisibilityValue( prev_keywords )
+ < this._getVisibilityValue( keywords )
+ )
+ {
+ throw TypeError(
+ "Cannot de-escalate visibility of property '" + name + "'"
+ );
+ }
+ }
+
+ // do not allow overriding getters/setters
+ if ( prev_data && ( prev_data.get || prev_data.set ) )
+ {
+ throw TypeError(
+ "Cannot override getter/setter '" + name + "' with property"
+ );
+ }
+
+ // abstract properties do not make sense
+ if ( keywords[ 'abstract' ] )
+ {
+ throw TypeError(
+ "Property '" + name + "' cannot be declared as abstract"
+ );
+ }
+
+ // constants are static
+ if ( keywords[ 'static' ] && keywords[ 'const' ] )
+ {
+ throw TypeError(
+ "Static keyword cannot be used with const for property '" +
+ name + "'"
+ );
+ }
+
+ // properties are inherently virtual
+ if ( keywords['virtual'] )
+ {
+ throw TypeError( "Cannot declare property '" + name + "' as virtual" );
+ }
+};
+
+
+/**
+ * Performs common validations on getters/setters
+ *
+ * If a problem is found, an exception will be thrown.
+ *
+ * @param {string} name getter/setter name
+ * @param {Object.} keywords parsed keywords
+ *
+ * @return {undefined}
+ */
+exports.prototype.validateGetterSetter = function(
+ name, value, keywords, prev_data, prev_keywords
+)
+{
+ var prev = ( prev_data ) ? prev_data.member : null,
+ prev_gs = ( ( prev_data && ( prev_data.get || prev_data.set ) )
+ ? true
+ : false
+ )
+ ;
+
+ // abstract getters/setters are not yet supported
+ if ( keywords[ 'abstract' ] )
+ {
+ throw TypeError(
+ "Cannot declare getter/setter '" + name + "' as abstract"
+ );
+ }
+
+ // for const getters/setters, omit the setter
+ if ( keywords[ 'const' ] )
+ {
+ throw TypeError(
+ "Cannot declare const getter/setter '" + name + "'"
+ );
+ }
+
+ // virtual static does not make sense, as static methods cannot be
+ // overridden
+ if ( keywords[ 'virtual' ] && ( keywords[ 'static' ] ) )
+ {
+ throw TypeError(
+ "Cannot declare static method '" + name + "' as virtual"
+ );
+ }
+
+ if ( prev || prev_gs )
+ {
+ // perform this check first, as it will make more sense than those that
+ // follow, should this condition be satisfied
+ if ( prev_keywords && prev_keywords[ 'private' ] )
+ {
+ throw TypeError(
+ "Private member name '" + name + "' conflicts with supertype"
+ );
+ }
+
+ // To speed up the system we'll simply check for a getter/setter, rather
+ // than checking separately for methods/properties. This is at the
+ // expense of more detailed error messages. They'll live.
+ if ( !( prev_gs ) )
+ {
+ throw TypeError(
+ "Cannot override method or property '" + name +
+ "' with getter/setter"
+ );
+ }
+
+ if ( !( prev_keywords && prev_keywords[ 'virtual' ] ) )
+ {
+ throw TypeError(
+ "Cannot override non-virtual getter/setter '" + name + "'"
+ );
+ }
+
+ if ( !( keywords[ 'override' ] ) )
+ {
+ throw TypeError(
+ "Attempting to override getter/setter '" + name +
+ "' without 'override' keyword"
+ );
+ }
+
+ // do not permit visibility de-escalation
+ if ( this._getVisibilityValue( prev_keywords || {} )
+ < this._getVisibilityValue( keywords )
+ )
+ {
+ throw TypeError(
+ "Cannot de-escalate visibility of getter/setter '" + name + "'"
+ );
+ }
+ }
+ else if ( keywords[ 'override' ] )
+ {
+ // using the override keyword without a super method may indicate a bug
+ // in the user's code
+ this._warningHandler( Error(
+ "Getter/setter '" + name +
+ "' using 'override' keyword without super getter/setter"
+ ) );
+ }
+}
+
+
+/**
+ * Return the visibility level as a numeric value, where 0 is public and 2 is
+ * private
+ *
+ * @param {Object} keywords keywords to scan for visibility level
+ *
+ * @return {number} visibility level as a numeric value
+ */
+exports.prototype._getVisibilityValue = function( keywords )
+{
+ if ( keywords[ 'protected' ] )
+ {
+ return 1;
+ }
+ else if ( keywords[ 'private' ] )
+ {
+ return 2;
+ }
+ else
+ {
+ // default is public
+ return 0;
+ }
+}
+
+} )( module['MemberBuilderValidator'] = {}, '.' );
+/** VisibilityObjectFactory **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Contains visibility object factory
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+/**
+ * XXX: tightly coupled
+ */
+var util = require( __dirname + '/util' );
+
+
+/**
+ * Initializes visibility object factory
+ *
+ * The visibility object is the "magic" behind ease.js. This factory creates the
+ * object that holds the varying levels of visibility, which are swapped out and
+ * inherited depending on circumstance.
+ *
+ * @constructor
+ */
+module.exports = exports = function VisibilityObjectFactory()
+{
+ // permit omitting 'new' keyword
+ if ( !( this instanceof exports ) )
+ {
+ // module.exports instead of exports because Closure Compiler seems to
+ // be confused
+ return new module.exports();
+ }
+};
+
+
+/**
+ * Sets up properties
+ *
+ * This includes all members (including private). Private members will be set up
+ * in a separate object, so that they can be easily removed from the mix. That
+ * object will include the destination object in the prototype, so that the
+ * access should be transparent. This object is returned.
+ *
+ * Properties are expected in the following format. Note that keywords are
+ * ignored:
+ * { public: { prop: [ value, { keyword: true } ] } }
+ *
+ * @param {Object} dest destination object
+ * @param {Object} properties properties to copy
+ * @param {Object=} methods methods to copy
+ *
+ * @return {Object} object containing private members and dest as prototype
+ */
+exports.prototype.setup = function setup( dest, properties, methods )
+{
+ // create the private layer atop of the destination object
+ var obj = this._createPrivateLayer( dest, properties );
+
+ // initialize each of the properties for this instance to
+ // ensure we're not sharing references to prototype values
+ this._doSetup( dest, properties[ 'public' ] );
+
+ // Do the same for protected, but only if they do not exist already in
+ // public. The reason for this is because the property object is laid /atop/
+ // of the public members, meaning that a parent's protected members will
+ // take precedence over a subtype's overriding /public/ members. Uh oh.
+ this._doSetup( dest,
+ properties[ 'protected' ],
+ methods[ 'protected' ],
+ 'public'
+ );
+
+ // then add the private parts
+ this._doSetup( obj, properties[ 'private' ], methods[ 'private' ] );
+
+ return obj;
+};
+
+
+/**
+ * Add an extra layer atop the destination object, which will contain the
+ * private members
+ *
+ * The object provided will be used as the prototype for the new private layer,
+ * so the provided object will be accessible on the prototype chain.
+ *
+ * Subtypes may override this method to alter the functionality of the private
+ * visibility object (e.g. to prevent it from being created).
+ *
+ * @param {Object} atop_of object to add private layer atop of
+ * @param {Object} properties properties
+ *
+ * @return {Object} private layer with given object as prototype
+ */
+exports.prototype._createPrivateLayer = function( atop_of, properties )
+{
+ /** @constructor */
+ var obj_ctor = function() {};
+ obj_ctor.prototype = atop_of;
+
+ // we'll be returning an instance, so that the prototype takes effect
+ var obj = new obj_ctor();
+
+ // All protected properties need to be proxied from the private object
+ // (which will be passed as the context) to the object containing protected
+ // values. Otherwise, the protected property values would be set on the
+ // private object, making them inaccessible to subtypes.
+ this.createPropProxy( atop_of, obj, properties[ 'protected' ] );
+
+ return obj;
+};
+
+
+/**
+ * Set up destination object by copying over properties and methods
+ *
+ * @param {Object} dest destination object
+ * @param {Object} properties properties to copy
+ * @param {Object} methods methods to copy
+ * @param {boolean} unless_keyword do not set if keyword is set on existing
+ * method
+ *
+ * @return {undefined}
+ */
+exports.prototype._doSetup = function(
+ dest, properties, methods, unless_keyword
+)
+{
+ var hasOwn = Array.prototype.hasOwnProperty,
+ pre = null;
+
+ // copy over the methods
+ if ( methods !== undefined )
+ {
+ for ( var method_name in methods )
+ {
+ if ( hasOwn.call( methods, method_name ) )
+ {
+ pre = dest[ method_name ];
+
+ // If requested, do not copy the method over if it already
+ // exists in the destination object. Don't use hasOwn here;
+ // unnecessary overhead and we want to traverse any prototype
+ // chains. We do not check the public object directly, for
+ // example, because we need a solution that will work if a proxy
+ // is unsupported by the engine.
+ //
+ // Also note that we need to allow overriding if it exists in
+ // the protected object (we can override protected with
+ // protected). This is the *last* check to ensure a performance
+ // hit is incured *only* if we're overriding protected with
+ // protected.
+ if ( !unless_keyword
+ || ( pre === undefined )
+ || !( pre.___$$keywords$$[ unless_keyword ] )
+ )
+ {
+ dest[ method_name ] = methods[ method_name ];
+ }
+ }
+ }
+ }
+
+ // initialize private/protected properties and store in instance data
+ for ( var prop in properties )
+ {
+ if ( hasOwn.call( properties, prop ) )
+ {
+ dest[ prop ] = util.clone( properties[ prop ][ 0 ] );
+ }
+ }
+}
+
+
+/**
+ * Creates a proxy for all given properties to the given base
+ *
+ * The proxy uses getters/setters to forward all calls to the base. The
+ * destination object will be used as the proxy. All properties within props
+ * will be used proxied.
+ *
+ * To summarize: for each property in props, all gets and sets will be forwarded
+ * to base.
+ *
+ * Please note that this does not use the JS proxy implementation. That will be
+ * done in the future for engines that support it.
+ *
+ * @param {Object} base object to proxy to
+ * @param {Object} dest object to treat as proxy (set getters/setters on)
+ * @param {Object} props properties to proxy
+ *
+ * @return {Object} returns dest
+ */
+exports.prototype.createPropProxy = function( base, dest, props )
+{
+ var hasOwn = Object.prototype.hasOwnProperty;
+
+ for ( var prop in props )
+ {
+ if ( !( hasOwn.call( props, prop ) ) )
+ {
+ continue;
+ }
+
+ ( function( prop )
+ {
+ // just in case it's already defined, so we don't throw an error
+ dest[ prop ] = undefined;
+
+ // public properties, when set internally, must forward to the
+ // actual variable
+ Object.defineProperty( dest, prop, {
+ set: function( val )
+ {
+ base[ prop ] = val;
+ },
+
+ get: function()
+ {
+ return base[ prop ];
+ },
+
+ enumerable: true
+ } );
+ } ).call( null, prop );
+ }
+
+ return dest;
+};
+
+} )( module['VisibilityObjectFactory'] = {}, '.' );
+/** FallbackVisibilityObjectFactory **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Contains fallback visibility object factory
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+
+/**
+ * Initializes fallback visibility object factory
+ *
+ * Unlike the standard visibility object, fallback does not create various
+ * layers. This is for the simple fact that setting a value on one of the layers
+ * is not visible to layers beneath it (its prototypes). Fallback is necessary
+ * if proxy support or emulation (via ES5 getters/setters) is unavailable.
+ */
+module.exports = exports = function FallbackVisibilityObjectFactory()
+{
+ // permit omitting 'new' keyword
+ if ( !( this instanceof exports ) )
+ {
+ // module.exports for Closure Compiler
+ return new module.exports();
+ }
+};
+
+
+/**
+ * "Inherit" from VisibilityObjectFactory
+ */
+exports.prototype = require( __dirname + '/VisibilityObjectFactory' )();
+
+
+/**
+ * Do not create private visibility layer
+ *
+ * We're likely falling back because we cannot properly support the private
+ * visibility layer. Therefore, it will be omitted.
+ *
+ * @param {Object} atop_of will be returned, unmodified
+ * @param {Object} properties ignored
+ *
+ * @return {Object} provided object with no additional layer
+ */
+exports.prototype._createPrivateLayer = function( atop_of, properties )
+{
+ return atop_of;
+};
+
+
+/**
+ * Does not create property proxy
+ *
+ * The fallback implementation is used because proxies are not supported and
+ * cannot be emulated with getters/setters.
+ *
+ * @param {Object} base will be returned, unmodified
+ * @param {Object} dest ignored
+ * @param {Object} props ignored
+ *
+ * @return {Object} given base
+ */
+exports.prototype.createPropProxy = function( base, dest, props )
+{
+ return base;
+};
+
+} )( module['FallbackVisibilityObjectFactory'] = {}, '.' );
+/** VisibilityObjectFactoryFactory **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Contains factory for visibility object factory
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ * @package core
+ *
+ * XXX: Figure out how to resolve Closure Compiler's warning about shared type
+ * information
+ */
+
+// XXX: Tightly coupled
+var util = require( __dirname + '/util' ),
+
+ VisibilityObjectFactory = require( __dirname + '/VisibilityObjectFactory' ),
+
+ FallbackVisibilityObjectFactory =
+ require( __dirname + '/FallbackVisibilityObjectFactory' )
+;
+
+
+/**
+ * Responsible for instantiating the VisibilityObjectFactory appropriate for the
+ * runtime environment
+ *
+ * This prototype determines what class should be instantiated. If we are within
+ * an ECMAScript 5 environment, we can take full advantage of the standard
+ * visibility object implementation. Otherwise, we are unable to emulate proxies
+ * and must fall back on a less sophisticated implementation that sacrifices
+ * visibility support.
+ */
+exports.fromEnvironment = function()
+{
+ // if falling back, return fallback, otherwise standard
+ return ( util.definePropertyFallback() )
+ ? FallbackVisibilityObjectFactory()
+ : VisibilityObjectFactory()
+ ;
+};
+
+} )( module['VisibilityObjectFactoryFactory'] = {}, '.' );
+/** class **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Contains basic inheritance mechanism
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+var util = require( __dirname + '/util' ),
+ ClassBuilder = require( __dirname + '/ClassBuilder' ),
+
+ warn = require( __dirname + '/warn' ),
+ Warning = warn.Warning,
+
+ MethodWrapperFactory = require( __dirname + '/MethodWrapperFactory' ),
+ wrappers = require( __dirname + '/MethodWrappers' ).standard,
+
+ class_builder = ClassBuilder(
+ require( __dirname + '/MemberBuilder' )(
+ MethodWrapperFactory( wrappers.wrapNew ),
+ MethodWrapperFactory( wrappers.wrapOverride ),
+ MethodWrapperFactory( wrappers.wrapProxy ),
+ require( __dirname + '/MemberBuilderValidator' )(
+ function( warning )
+ {
+ warn.handle( Warning( warning ) );
+ }
+ )
+ ),
+ require( __dirname + '/VisibilityObjectFactoryFactory' )
+ .fromEnvironment()
+ )
+;
+
+
+/**
+ * This module may be invoked in order to provide a more natural looking class
+ * definition mechanism
+ *
+ * This may not be used to extend existing classes. To extend an existing class,
+ * use the class's extend() method. If unavailable (or extending a non-ease.js
+ * class/object), use the module's extend() method.
+ *
+ * @param {string|Object} namedef optional name or definition
+ * @param {Object=} def class definition if first argument is name
+ *
+ * @return {Function|Object} new class or staging object
+ */
+module.exports = function( namedef, def )
+{
+ var type = ( typeof namedef ),
+ result = null
+ ;
+
+ switch ( type )
+ {
+ // anonymous class
+ case 'object':
+ result = createAnonymousClass.apply( null, arguments );
+ break;
+
+ // named class
+ case 'string':
+ result = createNamedClass.apply( null, arguments );
+ break;
+
+ default:
+ // we don't know what to do!
+ throw TypeError(
+ "Expecting anonymous class definition or named class definition"
+ );
+ }
+
+ return result;
+};
+
+
+/**
+ * Creates a class, inheriting either from the provided base class or the
+ * default base class
+ *
+ * @param {Function|Object} baseordfn parent or definition object
+ * @param {Object=} dfn definition object if parent provided
+ *
+ * @return {Function} extended class
+ */
+module.exports.extend = function( baseordfn, dfn )
+{
+ return extend.apply( this, arguments );
+};
+
+
+/**
+ * Implements an interface or set of interfaces
+ *
+ * @param {...Function} interfaces interfaces to implement
+ *
+ * @return {Object} intermediate interface object
+ */
+module.exports.implement = function( interfaces )
+{
+ // implement on empty base
+ return createImplement(
+ null,
+ Array.prototype.slice.call( arguments )
+ );
+};
+
+
+/**
+ * Determines whether the provided object is a class created through ease.js
+ *
+ * @param {Object} obj object to test
+ *
+ * @return {boolean} true if class (created through ease.js), otherwise false
+ */
+module.exports.isClass = function( obj )
+{
+ obj = obj || {};
+
+ return ( obj.prototype instanceof ClassBuilder.ClassBase )
+ ? true
+ : false
+ ;
+};
+
+
+/**
+ * Determines whether the provided object is an instance of a class created
+ * through ease.js
+ *
+ * @param {Object} obj object to test
+ *
+ * @return {boolean} true if instance of class (created through ease.js),
+ * otherwise false
+ */
+module.exports.isClassInstance = function( obj )
+{
+ obj = obj || {};
+
+ return ( obj instanceof ClassBuilder.ClassBase )
+ ? true
+ : false;
+};
+
+
+/**
+ * Determines if the class is an instance of the given type
+ *
+ * The given type can be a class, interface, trait or any other type of object.
+ * It may be used in place of the 'instanceof' operator and contains additional
+ * enhancements that the operator is unable to provide due to prototypal
+ * restrictions.
+ *
+ * @param {Object} type expected type
+ * @param {Object} instance instance to check
+ *
+ * @return {boolean} true if instance is an instance of type, otherwise false
+ */
+module.exports.isInstanceOf = ClassBuilder.isInstanceOf;
+
+
+/**
+ * Alias for isInstanceOf()
+ *
+ * May read better in certain situations (e.g. Cat.isA( Mammal )) and more
+ * accurately conveys the act of inheritance, implementing interfaces and
+ * traits, etc.
+ */
+module.exports.isA = module.exports.isInstanceOf;
+
+
+/**
+ * Creates a new anonymous Class from the given class definition
+ *
+ * @param {Object} def class definition
+ *
+ * @return {Function} new anonymous class
+ */
+function createAnonymousClass( def )
+{
+ // ensure we have the proper number of arguments (if they passed in
+ // too many, it may signify that they don't know what they're doing,
+ // and likely they're not getting the result they're looking for)
+ if ( arguments.length > 1 )
+ {
+ throw Error(
+ "Expecting one argument for anonymous Class definition; " +
+ arguments.length + " given."
+ );
+ }
+
+ return extend( def );
+}
+
+
+/**
+ * Creates a new named Class from the given class definition
+ *
+ * @param {string} name class name
+ * @param {Object} def class definition
+ *
+ * @return {Function|Object} new named class or staging object if definition
+ * was not provided
+ */
+function createNamedClass( name, def )
+{
+ // if too many arguments were provided, it's likely that they're
+ // expecting some result that they're not going to get
+ if ( arguments.length > 2 )
+ {
+ throw Error(
+ "Expecting at most two arguments for definition of named Class '" +
+ name + "'; " + arguments.length + " given."
+ );
+ }
+
+ // if no definition was given, return a staging object, to apply the name to
+ // the class once it is actually created
+ if ( def === undefined )
+ {
+ return createStaging( name );
+ }
+ // the definition must be an object
+ else if ( typeof def !== 'object' )
+ {
+ throw TypeError(
+ "Unexpected value for definition of named Class '" + name +
+ "'; object expected"
+ );
+ }
+
+ // add the name to the definition
+ def.__name = name;
+
+ return extend( def );
+}
+
+
+/**
+ * Creates a staging object to stage a class name
+ *
+ * The class name will be applied to the class generated by operations performed
+ * on the staging object. This allows applying names to classes that need to be
+ * extended or need to implement interfaces.
+ *
+ * @param {string} cname desired class name
+ *
+ * @return {Object} object staging the given class name
+ */
+function createStaging( cname )
+{
+ return {
+ extend: function()
+ {
+ var args = Array.prototype.slice.apply( arguments );
+
+ // extend() takes a maximum of two arguments. If only one
+ // argument is provided, then it is to be the class definition.
+ // Otherwise, the first argument is the supertype and the second
+ // argument is the class definition. Either way you look at it,
+ // the class definition is always the final argument.
+ //
+ // We want to add the name to the definition.
+ args[ args.length - 1 ].__name = cname;
+
+ return extend.apply( null, args );
+ },
+
+ implement: function()
+ {
+ // implement on empty base, providing the class name to be used once
+ // extended
+ return createImplement(
+ null,
+ Array.prototype.slice.call( arguments ),
+ cname
+ );
+ }
+ };
+}
+
+
+/**
+ * Creates an intermediate object to permit implementing interfaces
+ *
+ * This object defers processing until extend() is called. This intermediate
+ * object ensures that a usable class is not generated until after extend() is
+ * called, as it does not make sense to create a class without any
+ * body/definition.
+ *
+ * @param {Object} base base class to implement atop of, or null
+ * @param {Array} ifaces interfaces to implement
+ * @param {string=} cname optional class name once extended
+ *
+ * @return {Object} intermediate implementation object
+ */
+function createImplement( base, ifaces, cname )
+{
+ // Defer processing until after extend(). This also ensures that implement()
+ // returns nothing usable.
+ return {
+ extend: function()
+ {
+ var args = Array.prototype.slice.call( arguments ),
+ def = args.pop(),
+ ext_base = args.pop()
+ ;
+
+ // if any arguments remain, then they likely misunderstood what this
+ // method does
+ if ( args.length > 0 )
+ {
+ throw Error(
+ "Expecting no more than two arguments for extend()"
+ );
+ }
+
+ // if a base was already provided for extending, don't allow them to
+ // give us yet another one (doesn't make sense)
+ if ( base && ext_base )
+ {
+ throw Error(
+ "Cannot override parent " + base.toString() + " with " +
+ ext_base.toString() + " via extend()"
+ );
+ }
+
+ // if a name was provided, use it
+ if ( cname )
+ {
+ def.__name = cname;
+ }
+
+ // If a base was provided when createImplement() was called, use
+ // that. Otherwise, use the extend() base passed to this function.
+ // If neither of those are available, extend from an empty class.
+ ifaces.push( base || ext_base || extend( {} ) );
+
+ return extend.call( null,
+ implement.apply( this, ifaces ),
+ def
+ );
+ }
+ };
+}
+
+
+/**
+ * Mimics class inheritance
+ *
+ * This method will mimic inheritance by setting up the prototype with the
+ * provided base class (or, by default, Class) and copying the additional
+ * properties atop of it.
+ *
+ * The class to inherit from (the first argument) is optional. If omitted, the
+ * first argument will be considered to be the properties list.
+ *
+ * @param {Function|Object} _ parent or definition object
+ * @param {Object=} __ definition object if parent was provided
+ *
+ * @return {Function} extended class
+ */
+function extend( _, __ )
+{
+ // set up the new class
+ var new_class = class_builder.build.apply( class_builder, arguments );
+
+ // set up some additional convenience props
+ setupProps( new_class );
+
+ // lock down the new class (if supported) to ensure that we can't add
+ // members at runtime
+ util.freeze( new_class );
+
+ return new_class;
+}
+
+
+/**
+ * Implements interface(s) into an object
+ *
+ * This will copy all of the abstract methods from the interface and merge it
+ * into the given object.
+ *
+ * @param {Object} baseobj base object
+ * @param {...Function} interfaces interfaces to implement into dest
+ *
+ * @return {Object} destination object with interfaces implemented
+ */
+var implement = function( baseobj, interfaces )
+{
+ var args = Array.prototype.slice.call( arguments ),
+ dest = {},
+ base = args.pop(),
+ len = args.length,
+ arg = null,
+
+ implemented = [],
+ make_abstract = false
+ ;
+
+ // add each of the interfaces
+ for ( var i = 0; i < len; i++ )
+ {
+ arg = args[ i ];
+
+ // copy all interface methods to the class (does not yet deep copy)
+ util.propParse( arg.prototype, {
+ method: function( name, func, is_abstract, keywords )
+ {
+ dest[ 'abstract ' + name ] = func.definition;
+ make_abstract = true;
+ }
+ } );
+ implemented.push( arg );
+ }
+
+ // xxx: temporary
+ if ( make_abstract )
+ {
+ dest.___$$abstract$$ = true;
+ }
+
+ // create a new class with the implemented abstract methods
+ var class_new = module.exports.extend( base, dest );
+ ClassBuilder.getMeta( class_new ).implemented = implemented;
+
+ return class_new;
+}
+
+
+/**
+ * Sets up common properties for the provided function (class)
+ *
+ * @param {function()} func function (class) to set up
+ *
+ * @return {undefined}
+ */
+function setupProps( func )
+{
+ attachExtend( func );
+ attachImplement( func );
+}
+
+
+/**
+ * Attaches extend method to the given function (class)
+ *
+ * @param {Function} func function (class) to attach method to
+ *
+ * @return {undefined}
+ */
+function attachExtend( func )
+{
+ /**
+ * Shorthand for extending classes
+ *
+ * This method can be invoked on the object, rather than having to call
+ * Class.extend( this ).
+ *
+ * @param {Object} props properties to add to extended class
+ *
+ * @return {Object} extended class
+ */
+ util.defineSecureProp( func, 'extend', function( props )
+ {
+ return extend( this, props );
+ });
+}
+
+
+/**
+ * Attaches implement method to the given function (class)
+ *
+ * Please see the implement() export of this module for more information.
+ *
+ * @param {function()} func function (class) to attach method to
+ *
+ * @return {undefined}
+ */
+function attachImplement( func )
+{
+ util.defineSecureProp( func, 'implement', function()
+ {
+ return createImplement(
+ func,
+ Array.prototype.slice.call( arguments )
+ );
+ });
+}
+
+} )( module['class'] = {}, '.' );
+/** class_final **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Wrapper permitting the definition of final classes
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+var Class = require( __dirname + '/class' );
+
+
+/**
+ * Creates a final class
+ *
+ * @return {Function} final class
+ */
+exports = module.exports = function()
+{
+ markFinal( arguments );
+
+ // forward everything to Class
+ var result = Class.apply( this, arguments );
+
+ if ( !Class.isClass( result ) )
+ {
+ finalOverride( result );
+ }
+
+ return result;
+};
+
+
+/**
+ * Creates a final class from a class extend operation
+ *
+ * @return {Function} final class
+ */
+exports.extend = function()
+{
+ markFinal( arguments );
+ return Class.extend.apply( this, arguments );
+};
+
+
+/**
+ * Causes a definition to be flagged as final
+ *
+ * This function assumes the last argument to be the definition, which is the
+ * common case, and modifies the object referenced by that argument.
+ *
+ * @param {!Arguments} args arguments to parse
+ *
+ * @return {undefined}
+ */
+function markFinal( args )
+{
+ // the last argument _should_ be the definition
+ var dfn = args[ args.length - 1 ];
+
+ if ( typeof dfn === 'object' )
+ {
+ // mark as abstract
+ dfn.___$$final$$ = true;
+ }
+}
+
+
+/**
+ * Overrides object members to permit final classes
+ *
+ * @param {Object} obj object to override
+ *
+ * @return {undefined}
+ */
+function finalOverride( obj )
+{
+ var extend = obj.extend;
+
+ // wrap extend, applying the abstract flag
+ obj.extend = function()
+ {
+ markFinal( arguments );
+ return extend.apply( this, arguments );
+ };
+}
+
+} )( module['class_final'] = {}, '.' );
+/** FallbackMemberBuilder **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Handles building members (properties, methods) in a pre-ES5 environment
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+/**
+ * Supertype
+ */
+var MemberBuilder = require( __dirname + '/MemberBuilder' );
+
+/**
+ * Responsible for building class members
+ */
+module.exports = exports = function FallbackMemberBuilder(
+ wrap_method, wrap_override
+)
+{
+ // permit omitting 'new' keyword
+ if ( !( this instanceof module.exports ) )
+ {
+ return new module.exports( wrap_method, wrap_override );
+ }
+
+ // invoke parent constructor
+ module.exports.prototype.constructor.call( this,
+ wrap_method, wrap_override
+ );
+};
+
+// inherit from MemberBuilder
+module.exports.prototype = new MemberBuilder();
+module.exports.constructor = module.exports;
+
+
+/**
+ * Getters/setters are unsupported in a pre-ES5 environment
+ *
+ * Simply throw an exception, as it clearly represents that the developer did
+ * not account for the possibility that their software may have been executed in
+ * a pre-ES5 environment.
+ */
+exports.prototype.buildGetterSetter = function()
+{
+ throw Error( 'Getters/setters are unsupported in this environment' );
+};
+} )( module['FallbackMemberBuilder'] = {}, '.' );
+/** interface **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Contains interface module
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+var util = require( __dirname + '/util' ),
+
+ MethodWrapperFactory = require( __dirname + '/MethodWrapperFactory' ),
+ wrappers = require( __dirname + '/MethodWrappers' ).standard,
+
+ member_builder = require( __dirname + '/MemberBuilder' )(
+ MethodWrapperFactory( wrappers.wrapNew ),
+ MethodWrapperFactory( wrappers.wrapOverride ),
+ MethodWrapperFactory( wrappers.wrapProxy ),
+ require( __dirname + '/MemberBuilderValidator' )()
+ ),
+
+ Class = require( __dirname + '/class' )
+;
+
+
+/**
+ * This module may be invoked in order to provide a more natural looking
+ * interface definition
+ *
+ * Only new interfaces may be created using this method. They cannot be
+ * extended. To extend an existing interface, call its extend() method, or use
+ * the extend() method of this module.
+ *
+ * @param {string|Object} namedef optional name or definition
+ * @param {Object=} def interface definition if first arg is name
+ *
+ * @return {Function|Object} new interface or staging object
+ */
+module.exports = function( namedef, def )
+{
+ var type = ( typeof namedef ),
+ result = null
+ ;
+
+ switch ( type )
+ {
+ // anonymous interface
+ case 'object':
+ result = createAnonymousInterface.apply( null, arguments );
+ break;
+
+ // named class
+ case 'string':
+ result = createNamedInterface.apply( null, arguments );
+ break;
+
+ default:
+ // we don't know what to do!
+ throw TypeError(
+ "Expecting anonymous interface definition or named " +
+ "interface definition"
+ );
+ }
+
+ return result;
+};
+
+
+/**
+ * Creates an interface
+ *
+ * @return {Function} extended interface
+ */
+module.exports.extend = function()
+{
+ return extend.apply( this, arguments );
+};
+
+
+/**
+ * Determines whether the provided object is an interface created through
+ * ease.js
+ *
+ * @param {Object} obj object to test
+ *
+ * @return {boolean} true if interface (created through ease.js), otherwise
+ * false
+ */
+module.exports.isInterface = function( obj )
+{
+ obj = obj || {};
+
+ return ( obj.prototype instanceof Interface )
+ ? true
+ : false
+ ;
+};
+
+
+/**
+ * Default interface implementation
+ *
+ * @return {undefined}
+ */
+function Interface() {}
+
+
+/**
+ * Creates a new anonymous Interface from the given interface definition
+ *
+ * @param {Object} def interface definition
+ *
+ * @return {Function} new anonymous interface
+ */
+function createAnonymousInterface( def )
+{
+ // ensure we have the proper number of arguments (if they passed in
+ // too many, it may signify that they don't know what they're doing,
+ // and likely they're not getting the result they're looking for)
+ if ( arguments.length > 1 )
+ {
+ throw Error(
+ "Expecting one argument for Interface definition; " +
+ arguments.length + " given."
+ );
+ }
+
+ return extend( def );
+}
+
+
+/**
+ * Creates a new named interface from the given interface definition
+ *
+ * @param {string} name interface name
+ * @param {Object} def interface definition
+ *
+ * @return {Function} new named interface
+ */
+function createNamedInterface( name, def )
+{
+ // if too many arguments were provided, it's likely that they're
+ // expecting some result that they're not going to get
+ if ( arguments.length > 2 )
+ {
+ throw Error(
+ "Expecting two arguments for definition of named Interface '" +
+ name + "'; " + arguments.length + " given."
+ );
+ }
+
+ // the definition must be an object
+ if ( typeof def !== 'object' )
+ {
+ throw TypeError(
+ "Unexpected value for definition of named Interface '" +
+ name + "'; object expected"
+ );
+ }
+
+ // add the name to the definition
+ def.__name = name;
+
+ return extend( def );
+}
+
+
+var extend = ( function( extending )
+{
+ return function extend()
+ {
+ // ensure we'll be permitted to instantiate interfaces for the base
+ extending = true;
+
+ var args = Array.prototype.slice.call( arguments ),
+ props = args.pop() || {},
+ base = args.pop() || Interface,
+ prototype = new base(),
+ iname = '',
+
+ members = member_builder.initMembers(
+ prototype, prototype, prototype
+ )
+ ;
+
+ // grab the name, if one was provided
+ if ( iname = props.__name )
+ {
+ // we no longer need it
+ delete props.__name;
+ }
+
+ // sanity check
+ inheritCheck( prototype );
+
+ var new_interface = createInterface( iname );
+
+ try
+ {
+ util.propParse( props, {
+ assumeAbstract: true,
+
+ property: function()
+ {
+ // should never get to this point because of assumeAbstract
+ throw TypeError( 'Unexpected internal error' );
+ },
+
+ getset: function()
+ {
+ // should never get to this point because of assumeAbstract
+ throw TypeError( 'Unexpected internal error' );
+ },
+
+ method: function( name, value, is_abstract, keywords )
+ {
+ // all members must be public
+ if ( keywords[ 'protected' ] || keywords[ 'private' ] )
+ {
+ throw TypeError(
+ iname + " member " + name + " must be public"
+ );
+ }
+
+ member_builder.buildMethod(
+ members, null, name, value, keywords
+ );
+ }
+ } );
+ }
+ catch ( e )
+ {
+ // alter the message to include our name
+ e.message = "Failed to define interface " +
+ ( ( iname ) ? iname : '(anonymous)' ) + ": " + e.message
+ ;
+
+ // re-throw
+ throw e;
+ }
+
+ attachExtend( new_interface );
+ attachStringMethod( new_interface, iname );
+
+ new_interface.prototype = prototype;
+ new_interface.constructor = new_interface;
+
+ // freeze the interface (preventing additions), if supported
+ util.freeze( new_interface );
+
+ // we're done; let's not allow interfaces to be instantiated anymore
+ extending = false;
+
+ return new_interface;
+ };
+
+
+ /**
+ * Creates a new interface constructor function
+ *
+ * @param {string=} iname interface name
+ *
+ * @return {function()}
+ */
+ function createInterface( iname )
+ {
+ return function()
+ {
+ // allows us to extend the interface without throwing an exception
+ // (since the prototype requires an instance)
+ if ( !extending )
+ {
+ // only called if someone tries to create a new instance of an
+ // interface
+ throw Error(
+ "Interface" + ( ( iname ) ? ( iname + ' ' ) : '' ) +
+ " cannot be instantiated"
+ );
+ }
+ };
+ }
+} )( false );
+
+
+/**
+ * Assures that the parent object is a valid object to inherit from
+ *
+ * This method allows inheriting from any object (note that it will likely cause
+ * errors if not an interface), but will place restrictions on objects like
+ * Classes that do not make sense to inherit from. This will provide a more
+ * friendly error, with suggestions on how to resolve the issue, rather than a
+ * cryptic error resulting from inheritance problems.
+ *
+ * This method will throw an exception if there is a violation.
+ *
+ * @param {Object} prototype prototype to check for inheritance flaws
+ *
+ * @return {undefined}
+ */
+function inheritCheck( prototype )
+{
+ // if we're inheriting from another interface, then we're good
+ if ( !( prototype instanceof Interface ) )
+ {
+ throw new TypeError( "Interfaces may only extend other interfaces" );
+ }
+}
+
+
+/**
+ * Attaches extend method to the given function (interface)
+ *
+ * @param {Function} func function (interface) to attach method to
+ *
+ * @return {undefined}
+ */
+function attachExtend( func )
+{
+ /**
+ * Shorthand for extending interfaces
+ *
+ * This method can be invoked on the object, rather than having to call
+ * Interface.extend( this ).
+ *
+ * @param {Object} props properties to add to extended interface
+ *
+ * @return {Object} extended interface
+ */
+ util.defineSecureProp( func, 'extend', function( props )
+ {
+ return extend( this, props );
+ });
+}
+
+
+/**
+ * Provides more sane/useful output when interface is converted to a string
+ *
+ * @param {Object} func interface
+ * @param {string=} iname interface name
+ *
+ * @return {undefined}
+ */
+function attachStringMethod( func, iname )
+{
+ func.toString = ( iname )
+ ? function() { return '[object Interface <' + iname + '>]'; }
+ : function() { return '[object Interface]'; }
+ ;
+}
+
+} )( module['interface'] = {}, '.' );
+/** class_abstract **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Wrapper permitting the definition of abstract classes
+ *
+ * This doesn't actually introduce any new functionality. Rather, it sets a flag
+ * to allow abstract methods within a class, forcing users to clearly state
+ * that a class is abstract.
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+var Class = require( __dirname + '/class' );
+
+
+/**
+ * Creates an abstract class
+ *
+ * @return {Function} abstract class
+ */
+module.exports = exports = function()
+{
+ markAbstract( arguments );
+
+ // forward everything to Class
+ var result = Class.apply( this, arguments );
+
+ // if we're using the temporary object, then override its methods to permit
+ // abstract classes
+ if ( !Class.isClass( result ) )
+ {
+ abstractOverride( result );
+ }
+
+ return result;
+};
+
+
+/**
+ * Creates an abstract class from a class extend operation
+ *
+ * @return {Function} abstract class
+ */
+exports.extend = function()
+{
+ markAbstract( arguments );
+ return Class.extend.apply( this, arguments );
+};
+
+
+/**
+ * Creates an abstract class implementing the given members
+ *
+ * Simply wraps the class module's implement() method.
+ *
+ * @return {Object} abstract class
+ */
+exports.implement = function()
+{
+ var impl = Class.implement.apply( this, arguments );
+
+ abstractOverride( impl );
+ return impl;
+};
+
+
+/**
+ * Causes a definition to be flagged as abstract
+ *
+ * This function assumes the last argument to be the definition, which is the
+ * common case, and modifies the object referenced by that argument.
+ *
+ * @param {Arguments} args arguments to parse
+ *
+ * @return {undefined}
+ */
+function markAbstract( args )
+{
+ // the last argument _should_ be the definition
+ var dfn = args[ args.length - 1 ];
+
+ if ( typeof dfn === 'object' )
+ {
+ // mark as abstract
+ dfn.___$$abstract$$ = true;
+ }
+}
+
+
+/**
+ * Overrides object members to permit abstract classes
+ *
+ * @param {Object} obj object to override
+ *
+ * @return {Object} obj
+ */
+function abstractOverride( obj )
+{
+ var extend = obj.extend,
+ impl = obj.implement;
+
+ // wrap and apply the abstract flag, only if the method is defined (it may
+ // not be under all circumstances, e.g. after an implement())
+ impl && ( obj.implement = function()
+ {
+ return abstractOverride( impl.apply( this, arguments ) );
+ } );
+
+ // wrap extend, applying the abstract flag
+ obj.extend = function()
+ {
+ markAbstract( arguments );
+ return extend.apply( this, arguments );
+ };
+
+ return obj;
+}
+
+} )( module['class_abstract'] = {}, '.' );
+/** version **/
+( function( module, __dirname )
+{
+ var exports = module.exports = {};
+/**
+ * Provides version information
+ *
+ * Copyright (C) 2010,2011 Mike Gerwitz
+ *
+ * This file is part of ease.js.
+ *
+ * ease.js is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * @author Mike Gerwitz
+ */
+
+/*** DO NOT MODIFY; generated by verset ***/
+
+var major = 0,
+ minor = 2,
+ rev = 0,
+ suffix = 'dev',
+
+ version = [ major, minor, rev, suffix ];
+
+version.major = major;
+version.minor = minor;
+version.rev = rev;
+version.suffix = suffix;
+
+version.toString = function()
+{
+ return this.join( '.' )
+ .replace( /\.([^.]+)$/, '-$1' )
+ .replace( /-$/, '' );
+};
+
+module.exports = version;
+} )( module['version'] = {}, '.' );
+
+ // the following should match the exports of /index.js
+ ns_exports.Class = module['class'].exports;
+ ns_exports.AbstractClass = module['class_abstract'].exports;
+ ns_exports.FinalClass = module['class_final'].exports;
+ ns_exports.Interface = module['interface'].exports;
+ ns_exports.version = module['version'].exports;
+} )( easejs );
+
diff --git a/scripts/ease.min.js b/scripts/ease.min.js
deleted file mode 100644
index 2623d10..0000000
--- a/scripts/ease.min.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * Combined, minified redistributable ease.js file.
- * http://easejs.org/
- * Licensed under the GNU LGPL v3+
- * Copyright (C) 2010,2011 Mike Gerwitz
- */
-var easejs={};
-(function(x){var l={},j=function(b){var d=b.replace(/^\.?\/|[^/]*?\/\.\.\/|\.js$/,""),d=l[d];if(void 0===d)throw"[ease.js] Undefined module: "+b;return d.exports};(function(b){var d={"public":!0,"protected":!0,"private":!0,"static":!0,"abstract":!0,"const":!0,virtual:!0,override:!0};(b.exports={}).parseKeywords=function(a){var b=a,c=[],f={},a=""+a;if(/ /.test(a))for(var c=a.split(/\s+/),b=c.pop(),a=c.length,e="";a--;){e=c[a];if(!d[e])throw Error("Unexpected keyword for '"+b+"': "+e);f[e]=!0}return{name:b,
-keywords:f}}})(l.prop_parser={},".");(function(b,d){function a(c,e){for(var a=e.length;a--;)if(null===e[a].match(/^[a-z_][a-z0-9_]*$/i))throw SyntaxError("Member "+c+" contains invalid parameter '"+e[a]+"'");}function g(){var a;return e?function(a,e,b){try{Object.defineProperty(a,e,{value:b,enumerable:!1,writable:!1,configurable:!1})}catch(f){c.definePropertyFallback(!0),a[e]=b}}:function(c,a,e){c[a]=e}}var c=b.exports={},f=j(d+"/prop_parser").parseKeywords,e;a:{if("function"===typeof Object.defineProperty)try{Object.defineProperty({},
-"x",{});e=!0;break a}catch(h){}e=!1}c.freeze="function"===typeof Object.freeze?Object.freeze:function(){};c.definePropertyFallback=function(a){if(void 0===a)return!e;e=!a;c.defineSecureProp=g();return c};c.defineSecureProp=g();c.clone=function m(c,a){a=!!a;if(c instanceof Array){if(!a)return c.slice(0);for(var e=[],b=0,f=c.length;b"}:function(){return"#"})}var e=null,d=this;c.toString=
-a?function(){return a}:function(){return"(Class)"};return c};i.prototype.createAbstractCtor=function(a){var b=this,c=function(){if(!b._extending)throw Error("Abstract class "+(a||"(anonymous)")+" cannot be instantiated");};c.toString=a?function(){return a}:function(){return"(AbstractClass)"};return c};i.prototype._attachPropInit=function(a,b,c,e,d){var f=this;k.defineSecureProp(a,"__initProps",function(e){var e=!!e,h=a.___$$parent$$,h=h&&h.__initProps;"function"===typeof h&&h.call(this,!0);h=f._visFactory.createPropProxy(this,
-this.___$$vis$$,b["public"]);h=this.___$$vis$$[d]=f._visFactory.setup(h,b,c);e||k.defineSecureProp(h,"__inst",this)})};i.getMethodInstance=function(a,b){var c=a.___$$vis$$;return a.__iid&&c?c[b]:null}})(l.ClassBuilder={},".");(function(b){var d=b.exports={};b.exports=d=function(a){if(!(this instanceof d))return new b.exports(a);this._factory=a};d.prototype.wrapMethod=function(a,b,c,d){return this._factory(a,b,c,d)}})(l.MethodWrapperFactory={},".");(function(b){(b.exports={}).standard={wrapOverride:function(b,
-a,g,c){return function(){var f=c(this,g)||this,e=void 0;f.__super=a;e=b.apply(f,arguments);f.__super=void 0;return e===f?this:e}},wrapNew:function(b,a,g,c){return function(){var a=c(this,g)||this,e=void 0,e=b.apply(a,arguments);return e===a?this:e}}}})(l.MethodWrappers={},".");(function(b,d){function a(a,b,c){var e=function(){throw TypeError("Only one access modifier may be used for definition of '"+c+"'");};if(b["private"])return(b["public"]||b["protected"])&&e(),a["private"];if(b["protected"])return(b["public"]||
-b["private"])&&e(),a["protected"];(b["private"]||b["protected"])&&e();return a["public"]}function g(a,b,c){for(var d=e.length,p=null;d--;)if(p=f.getPropertyDescriptor(a[e[d]],b,!0))return{get:p.get,set:p.set,member:p.value};return void 0!==c?(a=c.___$$methods$$,c=c.___$$props$$,a&&g(a,b)||c&&g(c,b)||null):null}var c=b.exports={},f=j(d+"/util");j(d+"/warn");var e=["public","protected","private"];b.exports=function(a,c,e){if(!(this instanceof b.exports))return new b.exports(a,c,e);this._wrapMethod=
-a;this._wrapOverride=c;this._validate=e};c=b.exports.prototype;c.initMembers=function(a,b,c){return{"public":a||{},"protected":b||{},"private":c||{}}};c.buildMethod=function(b,c,e,d,f,q,i,k){var j=(k=(c=g(b,e,k))?c.member:null)&&k.___$$keywords$$,b=a(b,f,e);this._validate.validateMethod(e,d,f,c,j);if(k)if(f.override||j["abstract"])d=this._overrideMethod(k,d,q,i);else throw Error("Method hiding not yet implemented (we should never get here; bug).");else d=f["abstract"]?d:this._overrideMethod(null,
-d,q,i);b[e]=d;b[e].___$$keywords$$=f};c.buildProp=function(b,c,e,d,f,q){q=(c=g(b,e,q))?c.member:null;this._validate.validateProperty(e,d,f,c,q?q[1]:null);a(b,f,e)[e]=[d,f]};c.buildGetterSetter=function(b,c,e,d,f,q,i){c=g(b,e,i);this._validate.validateGetterSetter(e,{},q,c,c&&c.get?c.get.___$$keywords$$:null);d.___$$keywords$$=q;Object.defineProperty(a(b,q,e),e,{get:d,set:f,enumerable:!0,configurable:!1})};c._overrideMethod=function(a,b,c,e){var d=null,d=(a?this._wrapOverride:this._wrapMethod).wrapMethod(b,
-a,e,c||function(){});f.defineSecureProp(d,"__length",b.__length||b.length);return d};c._getVisibilityValue=function(a){return a["protected"]?1:a["private"]?2:0}})(l.MemberBuilder={},".");(function(b){var d=b.exports={};b.exports=d=function(a){if(!(this instanceof b.exports))return new b.exports(a);this._warningHandler=a||function(){}};d.prototype.validateMethod=function(a,b,c,d,e){var h=d?d.member:null;if(c["abstract"]&&c["private"])throw TypeError("Method '"+a+"' cannot be both private and abstract");
-if(c["const"])throw TypeError("Cannot declare method '"+a+"' as constant; keyword is redundant");if(c.virtual&&c["static"])throw TypeError("Cannot declare static method '"+a+"' as virtual");if(d&&(d.get||d.set))throw TypeError("Cannot override getter/setter '"+a+"' with method");if(h){if(e["private"])throw TypeError("Private member name '"+a+"' conflicts with supertype");if("function"!==typeof h)throw TypeError("Cannot override property '"+a+"' with method");if(c.override&&!e.virtual)throw TypeError("Cannot override non-virtual method '"+
-a+"'");if(c["abstract"]&&!e["abstract"])throw TypeError("Cannot override concrete method '"+a+"' with abstract method");if((b.__length||b.length)<(h.__length||h.length))throw TypeError("Declaration of method '"+a+"' must be compatible with that of its supertype");if(this._getVisibilityValue(e)]"}:function(){return"[object Interface]"}}b.exports={};var h=j(d+"/util"),l=j(d+"/MethodWrapperFactory"),m=j(d+"/MethodWrappers").standard,r=j(d+"/MemberBuilder")(l(m.wrapNew),
-l(m.wrapOverride),j(d+"/MemberBuilderValidator")());j(d+"/class");b.exports=function(a,b){var d=null;switch(typeof a){case "object":d=g.apply(null,arguments);break;case "string":d=c.apply(null,arguments);break;default:throw TypeError("Expecting anonymous interface definition or named interface definition");}return d};b.exports.extend=function(){return p.apply(this,arguments)};b.exports.isInterface=function(b){b=b||{};return b.prototype instanceof a?!0:!1};var p=function(b){function c(a){return function(){if(!b)throw Error("Interface"+
-(a?a+" ":"")+" cannot be instantiated");}}return function(){b=!0;var d=Array.prototype.slice.call(arguments),g=d.pop()||{},d=new (d.pop()||a),j="",l=r.initMembers(d,d,d);(j=g.__name)&&delete g.__name;if(!(d instanceof a))throw new TypeError("Interfaces may only extend other interfaces");var n=c(j);try{h.propParse(g,{assumeAbstract:!0,property:function(){throw TypeError("Unexpected internal error");},getset:function(){throw TypeError("Unexpected internal error");},method:function(a,b,c,d){if(d["protected"]||
-d["private"])throw TypeError(j+" member "+a+" must be public");r.buildMethod(l,null,a,b,d)}})}catch(m){throw m.message="Failed to define interface "+(j?j:"(anonymous)")+": "+m.message,m;}f(n);e(n,j);n.prototype=d;n.constructor=n;h.freeze(n);b=!1;return n}}(!1)})(l["interface"]={},".");(function(b){b.exports={};var d=[0,1,0];d.major=0;d.minor=1;d.rev=0;d.toString=function(){return this.join(".")};b.exports=d})(l.version={},".");x.Class=l["class"].exports;x.AbstractClass=l.class_abstract.exports;x.FinalClass=
-l.class_final.exports;x.Interface=l["interface"].exports;x.version=l.version.exports})(easejs);