From f4a61bf28ae9428054cd3c47b0a478fb4a01704d Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Sat, 19 Jan 2013 22:59:02 -0500 Subject: [PATCH] ease.js version update (0.1.1); using non-minified version for development A build can use the minified version for distribution --- index.html | 2 +- scripts/ease.js | 4987 +++++++++++++++++++++++++++++++++++++++++++ scripts/ease.min.js | 55 - 3 files changed, 4988 insertions(+), 56 deletions(-) create mode 100644 scripts/ease.js delete mode 100644 scripts/ease.min.js 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);