/*jslint es5: true, plusplus: true, white: true, indent: 2, maxlen: 90 */
/*global $wk$ */
/**
* @author Wolfgang Kowarschick
* @copyright 2012-2013, Wolfgang Kowarschick
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted under the terms of the
* Creative Commons License Attribution-NonCommercial-ShareAlike 3.0 Unported
* (CC BY-NC-SA 3.0: http://creativecommons.org/licenses/by-nc-sa/3.0/).
*/
////////////////////////////////////////////////////////////////////////////////
// Class "$wk$.WKcClass"
////////////////////////////////////////////////////////////////////////////////
$wk$(
function($)
{ "use strict";
var i = 0,
mixin = $.mixin,
pkg = $.pkg,
moduleAdd = $.moduleAdd,
l_dummy = {},
l_prototype,
l_wrapper,
l_class,
l_class_props = ["classFullName", "classID", "classPath", "parent"],
ANNOTATION_CLASS = "cls",
ANNOTATION_PROPERTY = "prop",
ANNOTATION_METHOD = "meth";
/**
* Temporarily defines a <code>$super$</code> method,
* before a method is invoked.
*
* @private
* @function
* @see http://ejohn.org/blog/simple-javascript-inheritance/#postcomment
*/
function f_wrapper(p_key, p_f)
{ return function()
{ var l_super = this.$super$, l_result;
this.$super$ = this.parent[p_key];
l_result = p_f.apply(this, arguments);
this.$super$ = l_super;
return l_result;
};
}
/**
* Sets the standard class properties.
*
* @private
* @function
*/
function f_class_properties(p_class, p_class_prop_values)
{ var l_key = null,
l_prototype = p_class.prototype;
p_class.fullName = p_class_prop_values[0];
p_class.id = p_class_prop_values[1];
p_class.path = p_class_prop_values[2];
l_prototype.constructor = p_class;
for (l_key in p_class_prop_values)
{ if (p_class_prop_values.hasOwnProperty(l_key))
{ Object.defineProperty(l_prototype,
l_class_props[l_key],
{ configurable: false,
enumerable: false,
value: p_class_prop_values[l_key],
writable: false
}
);
}
}
}
/**
* The meta class <code>WKcClass</code> can be used to create classes.
*
* @class
* @name $wk$.WKcClass
*
* @param {String} [p_args.fullName] The full name of the class:
* path + "." + id.
* @param {String} [p_args.id] A unique identifier of the class.
* @param {String} [p_args.path] The path, where the class is stored.
* @param {Object} [p_args.methods] The methods of the class
* (incl. real getters and setters).
* @param {Object} [p_args.observers] The observer methods of the class;
* <code>this</code> denotes the
* object itself.
* @param {Class} [p_args.inherit] A superclass, the methods
* of which are inherited.
* @param {Class|Array}
* [p_args.mixin] A class or an array of classes, the
* methods of which are directly copied
* into the prototype object of the
* new class.
* @param {Object} [p_args.staticMethods]
* The static methods of the class
* (incl. real getters and setters).
* @param {Object} [p_args.staticObservers]
* The static observer methods of the
* class. <code>this</code> denotes
* the class itself.
* @param {Class|Array}
* [p_args.staticMixin] A class or an array of classes, the
* class methods of which are directly
* copied into the new class object.
* @returns {Object} A new class (object).
*/
l_class =
function(p_args)
{ p_args = p_args || {};
var l_max,
f_aux,
l_key = null,
l_full_id = p_args.fullName || "ANONYMOUS",
l_id = p_args.id
|| l_full_id.match("[^.]*$")[0],
l_path = p_args.path
|| ( p_args.fullName
&& l_full_id.substring(0, l_full_id.length
- l_id.length
- 1
)
),
l_methods = p_args.methods,
l_observers = p_args.observers,
f_observers = function(p_observers, p_object, p_this)
{ var l_key = null;
for (l_key in p_observers)
{ if (p_observers.hasOwnProperty(l_key))
{ p_object[l_key]
= p_observers[l_key].bind(p_this);
}
}
},
l_inherit = p_args.inherit,
l_mixin = p_args.mixin,
l_static_methods = p_args.staticMethods,
l_static_observers = p_args.staticObservers,
l_static_mixin = p_args.staticMixin,
Class = null;
// Replace, if necessary, the name of the super class
// by the super class itself.
if (typeof l_inherit === "string")
{ l_inherit = pkg(l_inherit, false); }
// TBD: Check that all observer methods are of type "Function".
// Auxiliary function used by the next two statements.
f_aux = function(p_class)
{ if (p_class && p_class.$__wk_observers__$)
{ if (!l_observers)
{ l_observers = p_class.$__wk_observers__$; }
else
{ mixin(p_class.$__wk_observers__$, l_observers); }
}
};
// Mixin observers of the super class.
f_aux(l_inherit);
// Mixin observers of the mixin classes.
if (l_mixin)
{ if (l_mixin instanceof Array)
{ for (i = 0, l_max = l_mixin.length; i < l_max; i++)
{ f_aux(l_mixin[i]); }
}
else
{ f_aux(l_mixin); }
}
// The new class.
Class =
function(p_test, p_args)
{ if (!(this instanceof Class))
{ return new Class(l_dummy, arguments); }
var l_proto,
l_this = this;
if (l_observers)
{ l_proto = Object.create(Class.prototype);
l_this = Object.create(l_proto);
f_observers(l_observers, l_proto, l_this);
}
if (this.init)
{ this.init.apply(l_this, (p_test===l_dummy)?p_args||[]:arguments); }
return l_this;
};
// Store observer methods for inheriting them (by mixin!) in subclasses.
if (l_observers)
{ Object.defineProperty
(Class, "$__wk_observers__$", { enumarable: false,
configurable: false,
writable: false,
value: l_observers,
}
);
}
if (l_id && !p_args.fullName)
{ l_full_id = ((l_path && l_path.length > 0)
? (l_path + ".")
: ""
) + l_id;
}
if (p_args.fullName && p_args.fullName !== l_full_id)
{ throw new Error( "full name ("
+ p_args.fullName
+ ") != path + name ("
+ l_full_id
+ ")");
}
// Create the prototyp object of the new class ...
l_prototype = Class.prototype
= Object.create((l_inherit || Object).prototype);
// ... and add some attributes:
f_class_properties(Class, [l_full_id, l_id, l_path,
(l_inherit || Object).prototype
]
);
// Add the user defined methods to the prototype object.
mixin(l_methods, l_prototype);
// Add the methods of all classes that are to be copied into the prototype
// object.
if (l_mixin)
{ if (l_mixin instanceof Array)
{ for (i = 0, l_max = l_mixin.length; i < l_max; i++)
{ mixin(l_mixin[i].prototype, l_prototype); }
}
else
{ mixin(l_mixin.prototype, l_prototype);
}
}
// Add the init method to the prototype object.
l_prototype.init =
(l_methods && l_methods.init)
|| (l_inherit && l_inherit.prototype.init)
|| l_prototype.init
|| null;
// If the new class inherits methods from a super class,
// add SUPER method to each method that overrides an inherited method.
if (l_inherit)
{ for (l_key in l_prototype)
{ if (l_prototype.hasOwnProperty(l_key))
{ f_aux = l_prototype[l_key];
if ( f_aux instanceof Function
&& l_inherit.prototype[l_key]
&& l_key !== "constructor"
)
{ l_wrapper = l_prototype[l_key] = f_wrapper(l_key, f_aux);
l_wrapper.$original$ = f_aux;
}
}
}
}
// Copy the static methods into the class object itself.
mixin(l_static_methods, Class);
// Add the methods of all classes that are to be copied into the class
// object.
if (l_static_mixin)
{ if (l_static_mixin instanceof Array)
{ for (i = 0, l_max = l_static_mixin.length; i < l_max; i++)
{ mixin(l_static_mixin[i], Class); }
}
else
{ mixin(l_static_mixin, Class); }
}
// Add the static observers.
if (l_static_observers)
{ f_observers(l_static_observers, Class, Class); }
// Initialize the class object.
if (Class.init)
{ Class.init(); }
// Store the class object within the subpackage stated.
if (l_path !== undefined && l_path !== null)
{ pkg(l_path)[l_id] = Class; }
return Class;
};
/**
* @method
*
* @param {Object} p_obj An arbitray object.
*
* @returns {String} The class name of <code>p_obj</code> or
* <code>undefined</code> if it's an instrinsic object.
*
* @see {@link http://blog.magnetiq.com/post/514962277/finding-out-class-names-of-javascript-objects}
*/
l_class.className
= function(p_obj)
{ var l_constructor, l_arr;
if (p_obj && p_obj.constructor)
{ l_constructor = p_obj.constructor;
if (l_constructor.className)
{ return l_constructor.className; }
if ( l_constructor.toString)
{ l_arr = l_constructor.toString().match(/function\s*(\w+)/);
if (l_arr && l_arr.length === 2)
{ return l_arr[1]; }
}
}
return undefined;
};
f_class_properties(l_class, ["$wk$.WKcClass", "WKcClass", "$wk$"]);
pkg("$wk$").WKcClass = l_class;
moduleAdd({name: "$wk$.WKcClass",
module: { WKcClass: l_class },
// init: function(p_c)
// { console.log("$wk$.WKcClass ready"); p_c(); },
});
});
////////////////////////////////////////////////////////////////////////////////
// End of Class "$wk$.WKcClass"
////////////////////////////////////////////////////////////////////////////////