/*
---
name: "Declare"
description: "Contains the Class Function for easily creating, extending, and implementing reusable Classes."
license:
- "[GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)"
- "[MIT License](http://opensource.org/licenses/mit-license.php)"
requires:
- Core
- accessors
provides: declare
...
*/
var declare = (function(atom){
var
declare, methods,
accessors = atom.accessors.inherit,
factory = false,
prototyping = false,
mutators = [];
declare = function (declareName, Parent, params) {
if (prototyping) return this;
params = methods.prepareParams(declareName, Parent, params);
// line break for more user-friendly debug string
var Constructor = function ()
{ return methods.construct.call(this, Constructor, arguments) };
// <debug> - should be removed on production
if (params.name) {
Constructor = new Function('con', 'return {"' + params.name + '": ' +
function(){ return con.apply(this, arguments) }
+ '}["' + params.name + '"];')(Constructor);
}
// </debug>
for (var i = 0, l = mutators.length; i < l; i++) {
mutators[i].fn( Constructor, params[mutators[i].name] );
}
Constructor.prototype.constructor = Constructor;
if (params.declareName) methods.define( params.declareName, Constructor );
return Constructor;
};
declare.prototype.bindMethods = function (methods) {
var i;
if (typeof methods == 'string') {
if (typeof this[methods] == 'function') {
this[methods] = this[methods].bind(this);
}
return this;
}
if (!methods) {
for (i in this) this.bindMethods(i);
return this;
}
for (i = methods.length; i--;) this.bindMethods( methods[i] );
return this;
};
declare.prototype.toString = function () {
return '[object ' + (this.constructor.NAME || 'Declare') + ']';
};
declare.NAME = 'atom.declare';
declare.invoke = function () {
return this.factory( arguments );
};
declare.own = function (properties) {
methods.addTo(this, properties, this.NAME + '.');
return this;
};
declare.factory = function (args) {
factory = true;
return new this(args);
};
declare.castArguments = function (args) {
if (args == null) return null;
var constructor = this;
return (args != null && args[0] && args[0] instanceof constructor) ?
args[0] : args instanceof constructor ? args : new constructor( args );
};
methods = {
define: function (path, value) {
var key, part, target = atom.global;
path = path.split('.');
key = path.pop();
while (path.length) {
part = path.shift();
if (!target[part]) {
target = target[part] = {};
} else {
target = target[part];
}
}
target[key] = value;
},
mixin: function (target, items) {
if (!Array.isArray(items)) items = [ items ];
for (var i = 0, l = items.length; i < l; i++) {
methods.addTo( target.prototype, methods.proto(items[i]) );
}
return this;
},
addTo: function (target, source, name) {
var i, property;
if (source) for (i in source) if (source.hasOwnProperty(i)) {
if (!accessors(source, target, i) && source[i] != declare.config) {
property = source[i];
if (coreIsFunction(property)) {
if (name) property.path = name + i;
if (!property.previous && coreIsFunction(target[i])) {
property.previous = target[i];
}
}
target[i] = property;
}
}
return target;
},
prepareParams: function (declareName, Parent, params) {
if (typeof declareName != 'string') {
params = Parent;
Parent = declareName;
declareName = null;
}
if (params == null && typeof Parent != 'function') {
params = Parent;
Parent = null;
}
if (!params ) params = {};
if (!params.prototype ) params = { prototype: params };
if (!params.name ) params.name = declareName;
if (!params.declareName ) params.declareName = declareName;
if (!params.parent && Parent) params.parent = Parent;
if (!params.prototype.initialize) {
params.prototype.initialize = function () {
if (!params.parent) return;
return params.parent.prototype.initialize.apply(this, arguments);
};
}
return params;
},
proto: function (Fn) {
prototyping = true;
var result = new Fn;
prototyping = false;
return result;
},
construct: function (Constructor, args) {
if (factory) {
args = args[0];
factory = false;
}
if (prototyping) return this;
if (this instanceof Constructor) {
if (Constructor.NAME) this.Constructor = Constructor.NAME;
return this.initialize.apply(this, args);
} else {
return Constructor.invoke.apply(Constructor, args);
}
}
};
declare.config = {
methods: methods,
mutator: atom.core.overloadSetter(function (name, fn) {
mutators.push({ name: name, fn: fn });
return this;
})
};
declare.config
.mutator( 'parent', function (Constructor, parent) {
parent = parent || declare;
methods.addTo( Constructor, parent );
Constructor.prototype = methods.proto( parent );
Constructor.Parent = parent;
})
.mutator( 'mixin', function (Constructor, mixins) {
if (mixins) methods.mixin( Constructor, mixins );
})
.mutator( 'name', function (Constructor, name) {
if (!name) return;
Constructor.NAME = name;
})
.mutator( 'own', function (Constructor, properties) {
Constructor.own(properties);
})
.mutator( 'prototype', function (Constructor, properties) {
methods.addTo(Constructor.prototype, properties, Constructor.NAME + '#');
});
return atom.declare = declare;
})(atom);