/*jshint node:true, laxcomma:true, smarttabs: true, forin:false, unused: false */
'use strict';
/**
* Prototypal inheritance made easy - A slight modification on the prime libs
* @module tastypie/lib/class
* @author Eric Satterwhite
* @requires mout/object/hasOwn
* @requires mout/object/mixIn
* @requires mout/lang/createObject
* @requires mout/lang/kindOf
* @requires mout/lang/clone
* @requires mout/object/merge
**/
var hasOwn = require("mout/object/hasOwn")
, mixIn = require("mout/object/mixIn")
, create = require("mout/lang/createObject")
, clone = require('mout/lang/clone')
, kindOf = require("mout/lang/kindOf")
, isObject = require('mout/lang/isObject')
, merge = require('mout/object/merge')
, mutators = {
};
var hasDescriptors = true;
try {
Object.defineProperty({}, "~", {});
Object.getOwnPropertyDescriptor({}, "~");
} catch (e){
hasDescriptors = false;
}
// we only need to be able to implement "toString" and "valueOf" in IE < 9
var hasEnumBug = !({valueOf: 0}).propertyIsEnumerable("valueOf"),
buggy = ["toString", "valueOf"];
var verbs = /^constructor|inherits|mixin$/;
var implement = function(proto){
var mutator
, value
, descriptor
, prototype
;
prototype = this.prototype;
for (var key in proto){
if (key.match(verbs)){
continue;
}
if( mutators.hasOwnProperty( key ) ){
mutator = mutators[ key ];
value = proto[ key ];
value = mutator.call( this, value );
if( !value ){
continue;
}
this[ key ] = value;
}
if (hasDescriptors){
descriptor = Object.getOwnPropertyDescriptor(proto, key);
if (descriptor){
Object.defineProperty(prototype, key, descriptor);
continue;
}
}
prototype[key] = proto[key];
}
if (hasEnumBug){
for (var i = 0; (key = buggy[i]); i++){
value = proto[key];
if (value !== Object.prototype[key]){
prototype[key] = value;
}
}
}
return this;
};
var prime = function(proto){
if (kindOf(proto) === "Function"){
proto = {constructor: proto};
}
var superprime = proto.inherits;
// if our nice proto object has no own constructor property
// then we proceed using a ghosting constructor that all it does is
// call the parent's constructor if it has a superprime, else an empty constructor
// proto.constructor becomes the effective constructor
var constructor = (hasOwn(proto, "constructor")) ? proto.constructor : (superprime) ? function(){
return superprime.apply(this, arguments);
} : function(){};
if (superprime){
mixIn(constructor, superprime);
var superproto = superprime.prototype;
// inherit from superprime
var cproto = constructor.prototype = create(superproto);
// setting constructor.parent to superprime.prototype
// because it's the shortest possible absolute reference
constructor.parent = superproto;
cproto.constructor = constructor;
cproto.$class = constructor;
isObject(proto.options) && isObject(superproto.options) && (proto.options = merge(clone(superproto.options), proto.options));
}
if (!constructor.implement){
constructor.implement = implement;
}
var mixins = proto.mixin;
if (mixins){
if (kindOf(mixins) !== "Array"){
mixins = [mixins];
}
for (var i = 0; i < mixins.length; i++){
constructor.implement(create(mixins[i].prototype));
}
}
// implement proto and return constructor
return constructor.implement(proto);
};
prime.defineMutator = function(name ,fn ){
Object.defineProperty( mutators, name, {
enumerable: true
,configurable: false
,get: function( ){
return fn;
}
});
};
/**
* @constructor
* @alias module:tastypie/lib/class
* @param {Object} prototype an object to serve as the instance prototype
* @param {Function} [prototype.constructor]
* @param {Class|Class[]|Function|Function[]} [prototype.mixin]
* @param {Function|Class} [prototype.extends] A function or class definintion to server as the prototype to inherit from ( parent class )
* @example
var EventEmitter = require('events').EventEmitter
var Options = require('tastypie/lib/class/options');
MyClass = new Class({
extends: require('fs').Stream
,mixin:[ EventEmitter, Options ]
,options:{
isActive: false
}
,constructor: function( options ){
this.setOptions( options ) // from options mixin
console.log("im the constructor");
EventEmitter.call( this );
}
,work: function(){
console.log('I'm doing work)
this.emit('work', this.options.isActive )
}
});
*/
module.exports = prime;