Nickolay - Task.Joose.Stable-3.05
Name
Task.Joose.Stable - Stable Joose distribution, required only for bootstraping purposes.
DESCRIPTION
Since Joose testing tool (Test.Run) is written in Joose itself, the "chicken and egg" problem arise.
Task.Joose.Stable
is supposed to solve it. It just contain the version of Joose, which assumes to be "stable".
Some tools depends on this package instead of the main package - Joose.
SEE ALSO
http://openjsan.org/go/?l=Joose
General documentation for Joose
AUTHORS
Nickolay Platonov nplatonov@cpan.org
COPYRIGHT AND LICENSE
Copyright (c) 2009, Nickolay Platonov
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of Nickolay Platonov nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Joose = function () { throw "Modules may not be instantiated." } Joose.top = this Joose.VERSION = 3.01 Joose.AUTHORITY = 'jsan:NPLATONOV' // Static helpers for Arrays Joose.A = { each : function (array, func, scope) { scope = scope || this for (var i = 0, len = array.length; i < len; i++) if (func.call(scope, array[i], i) === false) return false }, exists : function (array, value) { for (var i = 0, len = array.length; i < len; i++) if (array[i] == value) return true return false }, map : function (array, func, scope) { scope = scope || this var res = [] for (var i = 0, len = array.length; i < len; i++) res.push( func.call(scope, array[i], i) ) return res }, grep : function (array, func) { var a = [] Joose.A.each(array, function (t) { if (func(t)) a.push(t) }) return a }, remove : function (array, removeEle) { var a = [] Joose.A.each(array, function (t) { if (t !== removeEle) a.push(t) }) return a } } // Static helpers for Strings Joose.S = { saneSplit : function (str, delimeter) { var res = (str || '').split(delimeter) if (res.length == 1 && !res[0]) res.shift() return res }, uppercaseFirst : function (string) { return string.substr(0, 1).toUpperCase() + string.substr(1, string.length - 1) } } // Static helpers for objects Joose.O = { each : function (object, func, scope) { scope = scope || this for (var i in object) if (func.call(scope, object[i], i) === false) return false if (Joose.is_IE) return Joose.A.each([ 'toString', 'constructor', 'hasOwnProperty' ], function (el) { if (object.hasOwnProperty(el)) return func.call(scope, object[el], el) }) }, eachOwn : function (object, func, scope) { scope = scope || this return Joose.O.each(object, function (value, name) { if (object.hasOwnProperty(name)) return func.call(scope, value, name) }, scope) }, copy : function (source, target) { target = target || {} Joose.O.each(source, function (value, name) { target[name] = value }) return target }, copyOwn : function (source, target) { target = target || {} Joose.O.eachOwn(source, function (value, name) { target[name] = value }) return target }, getMutableCopy : function (object) { var f = function () {} f.prototype = object return new f() }, extend : function (target, source) { return Joose.O.copy(source, target) }, isEmpty : function (object) { for (var i in object) if (object.hasOwnProperty(i)) return false return true }, isInstance: function (obj) { return obj && obj.meta && obj.constructor == obj.meta.c }, wantArray : function (obj) { if (obj instanceof Array) return obj return [ obj ] }, isFunction : function (obj) { return typeof obj == 'function' && obj.constructor != RegExp } } //initializers Joose.I = { Array : function () { return [] }, Object : function () { return {} }, Function : function () { return function () {} }, Now : function () { return new Date() } } Joose.is_IE = '\v' == 'v' Joose.is_NodeJS = Boolean(typeof process != 'undefined' && process.pid); Joose.Proto = function () { throw "Modules may not be instantiated." } Joose.Proto.Empty = function () { throw "Joose.Proto.Empty can't be instantiated" } Joose.Proto.Empty.meta = {}; (function () { Joose.Proto.Object = function () { throw "Joose.Proto.Object can't be instantiated" } var SUPER = function () { var self = SUPER.caller if (self == SUPERARG) self = self.caller if (!self.SUPER) throw "Invalid call to SUPER" return self.SUPER[self.methodName].apply(this, arguments) } var SUPERARG = function () { return this.SUPER.apply(this, arguments[0]) } Joose.Proto.Object.prototype = { SUPERARG : SUPERARG, SUPER : SUPER, INNER : function () { throw "Invalid call to INNER" }, BUILD : function (config) { return arguments.length == 1 && typeof config == 'object' && config || {} }, initialize: function () { }, toString: function () { return "a " + this.meta.name } } Joose.Proto.Object.meta = { constructor : Joose.Proto.Object, methods : Joose.O.copy(Joose.Proto.Object.prototype), attributes : {} } Joose.Proto.Object.prototype.meta = Joose.Proto.Object.meta })(); ;(function () { Joose.Proto.Class = function () { return this.initialize(this.BUILD.apply(this, arguments)) || this } var bootstrap = { VERSION : null, AUTHORITY : null, constructor : Joose.Proto.Class, superClass : null, name : null, attributes : null, methods : null, meta : null, c : null, defaultSuperClass : Joose.Proto.Object, BUILD : function (name, extend) { this.name = name return { __extend__ : extend || {} } }, initialize: function (props) { var extend = props.__extend__ this.VERSION = extend.VERSION this.AUTHORITY = extend.AUTHORITY delete extend.VERSION delete extend.AUTHORITY this.c = this.extractConstructor(extend) this.adaptConstructor(this.c) if (extend.constructorOnly) { delete extend.constructorOnly return } this.construct(extend) }, construct : function (extend) { if (!this.prepareProps(extend)) return var superClass = this.superClass = this.extractSuperClass(extend) this.processSuperClass(superClass) this.adaptPrototype(this.c.prototype) this.finalize(extend) }, finalize : function (extend) { this.processStem(extend) this.extend(extend) }, //if the extension returns false from this method it should re-enter 'construct' prepareProps : function (extend) { return true }, extractConstructor : function (extend) { var res = extend.hasOwnProperty('constructor') ? extend.constructor : this.defaultConstructor() delete extend.constructor return res }, extractSuperClass : function (extend) { var res = extend.isa || this.defaultSuperClass delete extend.isa return res }, processStem : function () { // '|| {}' for non-Joose superclasses var superMeta = this.superClass.meta || {} this.methods = Joose.O.getMutableCopy(superMeta.methods || {}) this.attributes = Joose.O.getMutableCopy(superMeta.attributes || {}) }, initInstance : function (instance, props) { Joose.O.copyOwn(props, instance) }, defaultConstructor: function () { return function () { var args = this.BUILD.apply(this, arguments) var thisMeta = this.meta thisMeta.initInstance(this, args) return thisMeta.hasMethod('initialize') && this.initialize(args) || this } }, processSuperClass: function (superClass) { this.c.prototype = Joose.O.getMutableCopy(superClass.prototype) this.c.superClass = superClass.prototype }, adaptConstructor: function (c) { c.meta = this if (!c.hasOwnProperty('toString')) c.toString = function () { return this.meta.name } }, adaptPrototype: function (proto) { //this will fix weird semantic of native "constructor" property to more intuitive (idea borrowed from Ext) proto.constructor = this.c proto.meta = this }, addMethod: function (name, func) { func.SUPER = this.superClass.prototype //chrome don't allow to redefine the "name" property func.methodName = name this.methods[name] = func this.c.prototype[name] = func }, addAttribute: function (name, init) { this.attributes[name] = init this.c.prototype[name] = init }, removeMethod : function (name) { delete this.methods[name] delete this.c.prototype[name] }, removeAttribute: function (name) { delete this.attributes[name] delete this.c.prototype[name] }, hasMethod: function (name) { return Boolean(this.methods[name]) }, hasAttribute: function (name) { return this.attributes[name] !== undefined }, hasOwnMethod: function (name) { return this.hasMethod(name) && this.methods.hasOwnProperty(name) }, hasOwnAttribute: function (name) { return this.hasAttribute(name) && this.attributes.hasOwnProperty(name) }, extend : function (props) { Joose.O.eachOwn(props, function (value, name) { if (name != 'meta' && name != 'constructor') if (Joose.O.isFunction(value) && !value.meta) this.addMethod(name, value) else this.addAttribute(name, value) }, this) }, subClassOf : function (classObject, extend) { return this.subClass(extend, null, classObject) }, subClass : function (extend, name, classObject) { extend = extend || {} extend.isa = classObject || this.c return new this.constructor(name, extend).c }, instantiate : function () { var f = function () {} f.prototype = this.c.prototype var obj = new f() return this.c.apply(obj, arguments) || obj } } //micro bootstraping Joose.Proto.Class.prototype = Joose.O.getMutableCopy(Joose.Proto.Object.prototype) Joose.O.extend(Joose.Proto.Class.prototype, bootstrap) Joose.Proto.Class.prototype.meta = new Joose.Proto.Class('Joose.Proto.Class', bootstrap) Joose.Proto.Class.meta.addMethod('isa', function (someClass) { var f = function () {} f.prototype = this.c.prototype return new f() instanceof someClass }) })(); Joose.Managed = function () { throw "Modules may not be instantiated." } Joose.Managed.Property = new Joose.Proto.Class('Joose.Managed.Property', { name : null, init : null, value : null, definedIn : null, initialize : function (props) { Joose.Managed.Property.superClass.initialize.call(this, props) this.computeValue() }, computeValue : function () { this.value = this.init }, //targetClass is still open at this stage preApply : function (targetClass) { }, //targetClass is already open at this stage postUnApply : function (targetClass) { }, apply : function (target) { target[this.name] = this.value }, isAppliedTo : function (target) { return target[this.name] == this.value }, unapply : function (from) { if (!this.isAppliedTo(from)) throw "Unapply of property [" + this.name + "] from [" + from + "] failed" delete from[this.name] }, cloneProps : function () { return { name : this.name, init : this.init, definedIn : this.definedIn } }, clone : function (name) { var props = this.cloneProps() props.name = name || props.name return new this.constructor(props) } }).c; Joose.Managed.Property.ConflictMarker = new Joose.Proto.Class('Joose.Managed.Property.ConflictMarker', { isa : Joose.Managed.Property, apply : function (target) { throw "Attempt to apply ConflictMarker [" + this.name + "] to [" + target + "]" } }).c; Joose.Managed.Property.Requirement = new Joose.Proto.Class('Joose.Managed.Property.Requirement', { isa : Joose.Managed.Property, apply : function (target) { if (!target.meta.hasMethod(this.name)) throw "Requirement [" + this.name + "], defined in [" + this.definedIn.definedIn.name + "] is not satisfied for class [" + target + "]" }, unapply : function (from) { } }).c; Joose.Managed.Property.Attribute = new Joose.Proto.Class('Joose.Managed.Property.Attribute', { isa : Joose.Managed.Property, slot : null, initialize : function () { Joose.Managed.Property.Attribute.superClass.initialize.apply(this, arguments) this.slot = this.name }, apply : function (target) { target.prototype[this.slot] = this.value }, isAppliedTo : function (target) { return target.prototype[this.slot] == this.value }, unapply : function (from) { if (!this.isAppliedTo(from)) throw "Unapply of property [" + this.name + "] from [" + from + "] failed" delete from.prototype[this.slot] }, getRawValueFrom : function (instance) { return instance[this.slot] }, setRawValueTo : function (instance, value) { instance[this.slot] = value } }).c; Joose.Managed.Property.MethodModifier = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier', { isa : Joose.Managed.Property, prepareWrapper : function () { throw "Abstract method [prepareWrapper] of " + this + " was called" }, apply : function (target) { var name = this.name var targetProto = target.prototype var isOwn = targetProto.hasOwnProperty(name) var original = targetProto[name] var superProto = target.meta.superClass.prototype var originalCall = isOwn ? original : function () { return superProto[name].apply(this, arguments) } var methodWrapper = this.prepareWrapper({ name : name, modifier : this.value, isOwn : isOwn, originalCall : originalCall, superProto : superProto, target : target }) if (isOwn) methodWrapper._original = original methodWrapper._contain = this.value targetProto[name] = methodWrapper }, isAppliedTo : function (target) { var targetCont = target.prototype[this.name] return targetCont && targetCont._contain == this.value }, unapply : function (from) { var name = this.name var fromProto = from.prototype var original = fromProto[name]._original if (!this.isAppliedTo(from)) throw "Unapply of method [" + name + "] from class [" + from + "] failed" //if modifier was applied to own method - restore it if (original) fromProto[name] = original //otherwise - just delete it, to reveal the inherited method else delete fromProto[name] } }).c; Joose.Managed.Property.MethodModifier.Override = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier.Override', { isa : Joose.Managed.Property.MethodModifier, prepareWrapper : function (params) { var modifier = params.modifier var originalCall = params.originalCall var superProto = params.superProto var superMetaConst = superProto.meta.constructor //call to Joose.Proto level, require some additional processing var isCallToProto = (superMetaConst == Joose.Proto.Class || superMetaConst == Joose.Proto.Object) && !(params.isOwn && originalCall.IS_OVERRIDE) var original = originalCall if (isCallToProto) original = function () { var beforeSUPER = this.SUPER this.SUPER = superProto.SUPER var res = originalCall.apply(this, arguments) this.SUPER = beforeSUPER return res } var override = function () { var beforeSUPER = this.SUPER this.SUPER = original var res = modifier.apply(this, arguments) this.SUPER = beforeSUPER return res } override.IS_OVERRIDE = true return override } }).c; Joose.Managed.Property.MethodModifier.Put = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier.Put', { isa : Joose.Managed.Property.MethodModifier.Override, prepareWrapper : function (params) { if (params.isOwn) throw "Method [" + params.name + "] is applying over something [" + params.originalCall + "] in class [" + params.target + "]" return Joose.Managed.Property.MethodModifier.Put.superClass.prepareWrapper.call(this, params) } }).c; Joose.Managed.Property.MethodModifier.After = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier.After', { isa : Joose.Managed.Property.MethodModifier, prepareWrapper : function (params) { var modifier = params.modifier var originalCall = params.originalCall return function () { var res = originalCall.apply(this, arguments) modifier.apply(this, arguments) return res } } }).c; Joose.Managed.Property.MethodModifier.Before = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier.Before', { isa : Joose.Managed.Property.MethodModifier, prepareWrapper : function (params) { var modifier = params.modifier var originalCall = params.originalCall return function () { modifier.apply(this, arguments) return originalCall.apply(this, arguments) } } }).c; Joose.Managed.Property.MethodModifier.Around = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier.Around', { isa : Joose.Managed.Property.MethodModifier, prepareWrapper : function (params) { var modifier = params.modifier var originalCall = params.originalCall var me var bound = function () { return originalCall.apply(me, arguments) } return function () { me = this var boundArr = [ bound ] boundArr.push.apply(boundArr, arguments) return modifier.apply(this, boundArr) } } }).c; Joose.Managed.Property.MethodModifier.Augment = new Joose.Proto.Class('Joose.Managed.Property.MethodModifier.Augment', { isa : Joose.Managed.Property.MethodModifier, prepareWrapper : function (params) { var AUGMENT = function () { //populate callstack to the most deep non-augment method var callstack = [] var self = AUGMENT do { callstack.push(self.IS_AUGMENT ? self._contain : self) self = self.IS_AUGMENT && (self._original || self.SUPER[self.methodName]) } while (self) //save previous INNER var beforeINNER = this.INNER //create new INNER this.INNER = function () { var innerCall = callstack.pop() return innerCall ? innerCall.apply(this, arguments) : undefined } //augment modifier results in hypotetical INNER call of the same method in subclass var res = this.INNER.apply(this, arguments) //restore previous INNER chain this.INNER = beforeINNER return res } AUGMENT.methodName = params.name AUGMENT.SUPER = params.superProto AUGMENT.IS_AUGMENT = true return AUGMENT } }).c; Joose.Managed.PropertySet = new Joose.Proto.Class('Joose.Managed.PropertySet', { isa : Joose.Managed.Property, properties : null, propertyMetaClass : Joose.Managed.Property, initialize : function (props) { Joose.Managed.PropertySet.superClass.initialize.call(this, props) //XXX this guards the meta roles :) this.properties = props.properties || {} }, addProperty : function (name, props) { var metaClass = props.meta || this.propertyMetaClass delete props.meta props.definedIn = this props.name = name return this.properties[name] = new metaClass(props) }, addPropertyObject : function (object) { return this.properties[object.name] = object }, removeProperty : function (name) { var prop = this.properties[name] delete this.properties[name] return prop }, haveProperty : function (name) { return this.properties[name] != null }, haveOwnProperty : function (name) { return this.haveProperty(name) && this.properties.hasOwnProperty(name) }, getProperty : function (name) { return this.properties[name] }, //includes inherited properties (probably you wants 'eachOwn', which process only "own" (including consumed from Roles) properties) each : function (func, scope) { Joose.O.each(this.properties, func, scope || this) }, eachOwn : function (func, scope) { Joose.O.eachOwn(this.properties, func, scope || this) }, //synonym for each eachAll : function (func, scope) { this.each(func, scope) }, cloneProps : function () { var props = Joose.Managed.PropertySet.superClass.cloneProps.call(this) props.propertyMetaClass = this.propertyMetaClass return props }, clone : function (name) { var clone = this.cleanClone(name) clone.properties = Joose.O.copyOwn(this.properties) return clone }, cleanClone : function (name) { var props = this.cloneProps() props.name = name || props.name return new this.constructor(props) }, alias : function (what) { var props = this.properties Joose.O.each(what, function (aliasName, originalName) { var original = props[originalName] if (original) this.addPropertyObject(original.clone(aliasName)) }, this) }, exclude : function (what) { var props = this.properties Joose.A.each(what, function (name) { delete props[name] }) }, beforeConsumedBy : function () { }, flattenTo : function (target) { var targetProps = target.properties this.eachOwn(function (property, name) { var targetProperty = targetProps[name] if (targetProperty instanceof Joose.Managed.Property.ConflictMarker) return if (targetProperty == null) { target.addPropertyObject(property) return } if (targetProperty == property) return target.removeProperty(name) target.addProperty(name, { meta : Joose.Managed.Property.ConflictMarker }) }, this) }, composeTo : function (target) { this.eachOwn(function (property, name) { if (!target.haveOwnProperty(name)) target.addPropertyObject(property) }) }, composeFrom : function () { if (!arguments.length) return var flattening = this.cleanClone() Joose.A.each(arguments, function (arg) { var isDescriptor = !(arg instanceof Joose.Managed.PropertySet) var propSet = isDescriptor ? arg.propertySet : arg propSet.beforeConsumedBy(this, flattening) if (isDescriptor) { if (arg.alias || arg.exclude) propSet = propSet.clone() if (arg.alias) propSet.alias(arg.alias) if (arg.exclude) propSet.exclude(arg.exclude) } propSet.flattenTo(flattening) }, this) flattening.composeTo(this) }, preApply : function (target) { this.eachOwn(function (property) { property.preApply(target) }) }, apply : function (target) { this.eachOwn(function (property) { property.apply(target) }) }, unapply : function (from) { this.eachOwn(function (property) { property.unapply(from) }) }, postUnApply : function (target) { this.eachOwn(function (property) { property.postUnApply(target) }) } }).c ; (function () { var __ID__ = 1 Joose.Managed.PropertySet.Mutable = new Joose.Proto.Class('Joose.Managed.PropertySet.Mutable', { isa : Joose.Managed.PropertySet, ID : null, derivatives : null, opened : null, composedFrom : null, initialize : function (props) { Joose.Managed.PropertySet.Mutable.superClass.initialize.call(this, props) //initially opened this.opened = 1 this.derivatives = {} this.ID = __ID__++ this.composedFrom = [] }, addComposeInfo : function () { this.ensureOpen() Joose.A.each(arguments, function (arg) { this.composedFrom.push(arg) var propSet = arg instanceof Joose.Managed.PropertySet ? arg : arg.propertySet propSet.derivatives[this.ID] = this }, this) }, removeComposeInfo : function () { this.ensureOpen() Joose.A.each(arguments, function (arg) { var i = 0 while (i < this.composedFrom.length) { var propSet = this.composedFrom[i] propSet = propSet instanceof Joose.Managed.PropertySet ? propSet : propSet.propertySet if (arg == propSet) { delete propSet.derivatives[this.ID] this.composedFrom.splice(i, 1) } else i++ } }, this) }, ensureOpen : function () { if (!this.opened) throw "Mutation of closed property set: [" + this.name + "]" }, addProperty : function (name, props) { this.ensureOpen() return Joose.Managed.PropertySet.Mutable.superClass.addProperty.call(this, name, props) }, addPropertyObject : function (object) { this.ensureOpen() return Joose.Managed.PropertySet.Mutable.superClass.addPropertyObject.call(this, object) }, removeProperty : function (name) { this.ensureOpen() return Joose.Managed.PropertySet.Mutable.superClass.removeProperty.call(this, name) }, composeFrom : function () { this.ensureOpen() return Joose.Managed.PropertySet.Mutable.superClass.composeFrom.apply(this, this.composedFrom) }, open : function () { this.opened++ if (this.opened == 1) { Joose.O.each(this.derivatives, function (propSet) { propSet.open() }) this.deCompose() } }, close : function () { if (!this.opened) throw "Unmatched 'close' operation on property set: [" + this.name + "]" if (this.opened == 1) { this.reCompose() Joose.O.each(this.derivatives, function (propSet) { propSet.close() }) } this.opened-- }, reCompose : function () { this.composeFrom() }, deCompose : function () { this.eachOwn(function (property, name) { if (property.definedIn != this) this.removeProperty(name) }, this) } }).c })() ; Joose.Managed.StemElement = function () { throw "Modules may not be instantiated." } Joose.Managed.StemElement.Attributes = new Joose.Proto.Class('Joose.Managed.StemElement.Attributes', { isa : Joose.Managed.PropertySet.Mutable, propertyMetaClass : Joose.Managed.Property.Attribute }).c ; Joose.Managed.StemElement.Methods = new Joose.Proto.Class('Joose.Managed.StemElement.Methods', { isa : Joose.Managed.PropertySet.Mutable, propertyMetaClass : Joose.Managed.Property.MethodModifier.Put, preApply : function () { }, postUnApply : function () { } }).c; Joose.Managed.StemElement.Requirements = new Joose.Proto.Class('Joose.Managed.StemElement.Requirements', { isa : Joose.Managed.PropertySet.Mutable, propertyMetaClass : Joose.Managed.Property.Requirement, alias : function () { }, exclude : function () { }, flattenTo : function (target) { this.each(function (property, name) { if (!target.haveProperty(name)) target.addPropertyObject(property) }) }, composeTo : function (target) { this.flattenTo(target) }, preApply : function () { }, postUnApply : function () { } }).c; Joose.Managed.StemElement.MethodModifiers = new Joose.Proto.Class('Joose.Managed.StemElement.MethodModifiers', { isa : Joose.Managed.PropertySet.Mutable, propertyMetaClass : null, addProperty : function (name, props) { var metaClass = props.meta delete props.meta props.definedIn = this props.name = name var modifier = new metaClass(props) if (!this.properties[name]) this.properties[name] = [] this.properties[name].push(modifier) return modifier }, addPropertyObject : function (object) { var name = object.name if (!this.properties[name]) this.properties[name] = [] this.properties[name].push(object) return object }, //remove only the last modifier removeProperty : function (name) { if (!this.haveProperty(name)) return undefined var modifier = this.properties[name].pop() //if all modifiers were removed - clearing the properties if (!this.properties[name].length) Joose.Managed.StemElement.MethodModifiers.superClass.removeProperty.call(this, name) return modifier }, alias : function () { }, exclude : function () { }, flattenTo : function (target) { var targetProps = target.properties this.each(function (modifiersArr, name) { var targetModifiersArr = targetProps[name] if (targetModifiersArr == null) targetModifiersArr = targetProps[name] = [] Joose.A.each(modifiersArr, function (modifier) { if (!Joose.A.exists(targetModifiersArr, modifier)) targetModifiersArr.push(modifier) }) }, this) }, composeTo : function (target) { this.flattenTo(target) }, deCompose : function () { this.each(function (modifiersArr, name) { var i = 0 while (i < modifiersArr.length) if (modifiersArr[i].definedIn != this) modifiersArr.splice(i, 1); else i++ }, this) }, preApply : function (target) { }, postUnApply : function (target) { }, apply : function (target) { this.each(function (modifiersArr, name) { Joose.A.each(modifiersArr, function (modifier) { modifier.apply(target) }) }) }, unapply : function (from) { this.each(function (modifiersArr, name) { for (var i = modifiersArr.length - 1; i >=0 ; i--) modifiersArr[i].unapply(from) }) } }).c; Joose.Managed.PropertySet.Composition = new Joose.Proto.Class('Joose.Managed.PropertySet.Composition', { isa : Joose.Managed.PropertySet.Mutable, propertyMetaClass : Joose.Managed.PropertySet.Mutable, processOrder : null, each : function (func, scope) { var props = this.properties Joose.A.each(this.processOrder, function (name) { func.call(scope || this, props[name], name) }, this) }, eachR : function (func, scope) { var props = this.properties var processOrder = this.processOrder for(var i = processOrder.length - 1; i >= 0; i--) func.call(scope || this, props[ processOrder[i] ], processOrder[i]) }, clone : function (name) { var clone = this.cleanClone(name) this.each(function (property) { clone.addPropertyObject(property.clone()) }) return clone }, alias : function (what) { this.each(function (property) { property.alias(what) }) }, exclude : function (what) { this.each(function (property) { property.exclude(what) }) }, flattenTo : function (target) { var targetProps = target.properties this.each(function (property, name) { var subTarget = targetProps[name] || target.addProperty(name, { meta : property.constructor }) property.flattenTo(subTarget) }) }, composeTo : function (target) { var targetProps = target.properties this.each(function (property, name) { var subTarget = targetProps[name] || target.addProperty(name, { meta : property.constructor }) property.composeTo(subTarget) }) }, deCompose : function () { this.eachR(function (property) { property.open() }) Joose.Managed.PropertySet.Composition.superClass.deCompose.call(this) }, reCompose : function () { Joose.Managed.PropertySet.Composition.superClass.reCompose.call(this) this.each(function (property) { property.close() }) }, unapply : function (from) { this.eachR(function (property) { property.unapply(from) }) } }).c ; Joose.Managed.Stem = new Joose.Proto.Class('Joose.Managed.Stem', { isa : Joose.Managed.PropertySet.Composition, targetMeta : null, attributesMC : Joose.Managed.StemElement.Attributes, methodsMC : Joose.Managed.StemElement.Methods, requirementsMC : Joose.Managed.StemElement.Requirements, methodsModifiersMC : Joose.Managed.StemElement.MethodModifiers, processOrder : [ 'attributes', 'methods', 'requirements', 'methodsModifiers' ], initialize : function (props) { Joose.Managed.Stem.superClass.initialize.call(this, props) var targetMeta = this.targetMeta this.addProperty('attributes', { meta : this.attributesMC, //it can be no 'targetMeta' in clones properties : targetMeta ? targetMeta.attributes : {} }) this.addProperty('methods', { meta : this.methodsMC, properties : targetMeta ? targetMeta.methods : {} }) this.addProperty('requirements', { meta : this.requirementsMC }) this.addProperty('methodsModifiers', { meta : this.methodsModifiersMC }) }, reCompose : function () { var c = this.targetMeta.c this.preApply(c) Joose.Managed.Stem.superClass.reCompose.call(this) this.apply(c) }, deCompose : function () { var c = this.targetMeta.c this.unapply(c) Joose.Managed.Stem.superClass.deCompose.call(this) this.postUnApply(c) } }).c ; Joose.Managed.Builder = new Joose.Proto.Class('Joose.Managed.Builder', { targetMeta : null, _buildStart : function (targetMeta, props) { targetMeta.stem.open() Joose.A.each([ 'trait', 'traits', 'removeTrait', 'removeTraits', 'does', 'doesnot', 'doesnt' ], function (builder) { if (props[builder]) { this[builder](targetMeta, props[builder]) delete props[builder] } }, this) }, _extend : function (props) { if (Joose.O.isEmpty(props)) return var targetMeta = this.targetMeta this._buildStart(targetMeta, props) Joose.O.eachOwn(props, function (value, name) { var handler = this[name] if (!handler) throw "Unknow builder [" + name + "] was used during extending of [" + targetMeta.c + "]" handler.call(this, targetMeta, value) }, this) this._buildComplete(targetMeta, props) }, _buildComplete : function (targetMeta, props) { targetMeta.stem.close() }, methods : function (targetMeta, info) { Joose.O.eachOwn(info, function (value, name) { targetMeta.addMethod(name, value) }) }, removeMethods : function (targetMeta, info) { Joose.A.each(info, function (name) { targetMeta.removeMethod(name) }) }, have : function (targetMeta, info) { Joose.O.eachOwn(info, function (value, name) { targetMeta.addAttribute(name, value) }) }, havenot : function (targetMeta, info) { Joose.A.each(info, function (name) { targetMeta.removeAttribute(name) }) }, havent : function (targetMeta, info) { this.havenot(targetMeta, info) }, after : function (targetMeta, info) { Joose.O.each(info, function (value, name) { targetMeta.addMethodModifier(name, value, Joose.Managed.Property.MethodModifier.After) }) }, before : function (targetMeta, info) { Joose.O.each(info, function (value, name) { targetMeta.addMethodModifier(name, value, Joose.Managed.Property.MethodModifier.Before) }) }, override : function (targetMeta, info) { Joose.O.each(info, function (value, name) { targetMeta.addMethodModifier(name, value, Joose.Managed.Property.MethodModifier.Override) }) }, around : function (targetMeta, info) { Joose.O.each(info, function (value, name) { targetMeta.addMethodModifier(name, value, Joose.Managed.Property.MethodModifier.Around) }) }, augment : function (targetMeta, info) { Joose.O.each(info, function (value, name) { targetMeta.addMethodModifier(name, value, Joose.Managed.Property.MethodModifier.Augment) }) }, removeModifier : function (targetMeta, info) { Joose.A.each(info, function (name) { targetMeta.removeMethodModifier(name) }) }, does : function (targetMeta, info) { Joose.A.each(Joose.O.wantArray(info), function (desc) { targetMeta.addRole(desc) }) }, doesnot : function (targetMeta, info) { Joose.A.each(Joose.O.wantArray(info), function (desc) { targetMeta.removeRole(desc) }) }, doesnt : function (targetMeta, info) { this.doesnot(targetMeta, info) }, trait : function () { this.traits.apply(this, arguments) }, traits : function (targetMeta, info) { if (targetMeta.firstPass) return if (!targetMeta.meta.isDetached) throw "Can't apply trait to not detached class" targetMeta.meta.extend({ does : info }) }, removeTrait : function () { this.removeTraits.apply(this, arguments) }, removeTraits : function (targetMeta, info) { if (!targetMeta.meta.isDetached) throw "Can't remove trait from not detached class" targetMeta.meta.extend({ doesnot : info }) } }).c; Joose.Managed.Class = new Joose.Proto.Class('Joose.Managed.Class', { isa : Joose.Proto.Class, stem : null, stemClass : Joose.Managed.Stem, stemClassCreated : false, builder : null, builderClass : Joose.Managed.Builder, builderClassCreated : false, isDetached : false, firstPass : true, // a special instance, which, when passed as 1st argument to constructor, signifies that constructor should // skips traits processing for this instance skipTraitsAnchor : {}, //build for metaclasses - collects traits from roles BUILD : function () { var sup = Joose.Managed.Class.superClass.BUILD.apply(this, arguments) var props = sup.__extend__ var traits = Joose.O.wantArray(props.trait || props.traits || []) delete props.trait delete props.traits Joose.A.each(Joose.O.wantArray(props.does || []), function (arg) { var role = (arg.meta instanceof Joose.Managed.Class) ? arg : arg.role if (role.meta.meta.isDetached) traits.push(role.meta.constructor) }) if (traits.length) props.traits = traits return sup }, initInstance : function (instance, props) { Joose.O.each(this.attributes, function (attribute, name) { if (attribute instanceof Joose.Managed.Attribute) attribute.initFromConfig(instance, props) else if (props.hasOwnProperty(name)) instance[name] = props[name] }) }, // we are using the same constructor for usual and meta- classes defaultConstructor: function () { return function (skipTraitsAnchor, params) { var thisMeta = this.meta var skipTraits = skipTraitsAnchor == thisMeta.skipTraitsAnchor var props = this.BUILD.apply(this, skipTraits ? params : arguments) // either looking for traits in __extend__ (meta-class) or in usual props (usual class) var extend = props.__extend__ || props var traits = extend.trait || extend.traits if (traits || extend.detached) { delete extend.trait delete extend.traits delete extend.detached if (!skipTraits) { var classWithTrait = thisMeta.subClass({ does : traits || [] }, thisMeta.name) var meta = classWithTrait.meta meta.isDetached = true return meta.instantiate(thisMeta.skipTraitsAnchor, arguments) } } thisMeta.initInstance(this, props) return thisMeta.hasMethod('initialize') && this.initialize(props) || this } }, finalize: function (extend) { Joose.Managed.Class.superClass.finalize.call(this, extend) this.stem.close() this.afterMutate() }, processStem : function () { Joose.Managed.Class.superClass.processStem.call(this) this.builder = new this.builderClass({ targetMeta : this }) this.stem = new this.stemClass({ name : this.name, targetMeta : this }) var builderClass = this.getClassInAttribute('builderClass') if (builderClass) { this.builderClassCreated = true this.addAttribute('builderClass', this.subClassOf(builderClass)) } var stemClass = this.getClassInAttribute('stemClass') if (stemClass) { this.stemClassCreated = true this.addAttribute('stemClass', this.subClassOf(stemClass)) } }, extend : function (props) { if (props.builder) { this.getBuilderTarget().meta.extend(props.builder) delete props.builder } if (props.stem) { this.getStemTarget().meta.extend(props.stem) delete props.stem } this.builder._extend(props) this.firstPass = false if (!this.stem.opened) this.afterMutate() }, getBuilderTarget : function () { var builderClass = this.getClassInAttribute('builderClass') if (!builderClass) throw "Attempt to extend a builder on non-meta class" return builderClass }, getStemTarget : function () { var stemClass = this.getClassInAttribute('stemClass') if (!stemClass) throw "Attempt to extend a stem on non-meta class" return stemClass }, getClassInAttribute : function (attributeName) { var attrClass = this.getAttribute(attributeName) if (attrClass instanceof Joose.Managed.Property.Attribute) attrClass = attrClass.value return attrClass }, addMethodModifier: function (name, func, type) { var props = {} props.init = func props.meta = type return this.stem.properties.methodsModifiers.addProperty(name, props) }, removeMethodModifier: function (name) { return this.stem.properties.methodsModifiers.removeProperty(name) }, addMethod: function (name, func, props) { props = props || {} props.init = func return this.stem.properties.methods.addProperty(name, props) }, addAttribute: function (name, init, props) { props = props || {} props.init = init return this.stem.properties.attributes.addProperty(name, props) }, removeMethod : function (name) { return this.stem.properties.methods.removeProperty(name) }, removeAttribute: function (name) { return this.stem.properties.attributes.removeProperty(name) }, hasMethod: function (name) { return this.stem.properties.methods.haveProperty(name) }, hasAttribute: function (name) { return this.stem.properties.attributes.haveProperty(name) }, hasOwnMethod: function (name) { return this.stem.properties.methods.haveOwnProperty(name) }, hasOwnAttribute: function (name) { return this.stem.properties.attributes.haveOwnProperty(name) }, getMethod : function (name) { return this.stem.properties.methods.getProperty(name) }, getAttribute : function (name) { return this.stem.properties.attributes.getProperty(name) }, eachRole : function (roles, func, scope) { Joose.A.each(roles, function (arg, index) { var role = (arg.meta instanceof Joose.Managed.Class) ? arg : arg.role func.call(scope || this, arg, role, index) }, this) }, addRole : function () { this.eachRole(arguments, function (arg, role) { this.beforeRoleAdd(role) var desc = arg //compose descriptor can contain 'alias' and 'exclude' fields, in this case actual reference should be stored //into 'propertySet' field if (role != arg) { desc.propertySet = role.meta.stem delete desc.role } else desc = desc.meta.stem this.stem.addComposeInfo(desc) }, this) }, beforeRoleAdd : function (role) { var roleMeta = role.meta if (roleMeta.builderClassCreated) this.getBuilderTarget().meta.extend({ does : [ roleMeta.getBuilderTarget() ] }) if (roleMeta.stemClassCreated) this.getStemTarget().meta.extend({ does : [ roleMeta.getStemTarget() ] }) if (roleMeta.meta.isDetached && !this.firstPass) this.builder.traits(this, roleMeta.constructor) }, beforeRoleRemove : function (role) { var roleMeta = role.meta if (roleMeta.builderClassCreated) this.getBuilderTarget().meta.extend({ doesnt : [ roleMeta.getBuilderTarget() ] }) if (roleMeta.stemClassCreated) this.getStemTarget().meta.extend({ doesnt : [ roleMeta.getStemTarget() ] }) if (roleMeta.meta.isDetached && !this.firstPass) this.builder.removeTraits(this, roleMeta.constructor) }, removeRole : function () { this.eachRole(arguments, function (arg, role) { this.beforeRoleRemove(role) this.stem.removeComposeInfo(role.meta.stem) }, this) }, getRoles : function () { var roles = [] Joose.A.each(this.stem.composedFrom, function (composeDesc) { //compose descriptor can contain 'alias' and 'exclude' fields, in this case actual reference is stored //into 'propertySet' field if (!(composeDesc instanceof Joose.Managed.PropertySet)) composeDesc = composeDesc.propertySet roles.push(composeDesc.targetMeta.c) }) return roles }, does : function (role) { var myRoles = this.getRoles() for (var i = 0; i < myRoles.length; i++) if (role == myRoles[i]) return true for (var i = 0; i < myRoles.length; i++) if (myRoles[i].meta.does(role)) return true var superMeta = this.superClass.meta // considering the case of inheriting from non-Joose classes if (this.superClass != Joose.Proto.Empty && superMeta && superMeta.meta && superMeta.meta.hasMethod('does')) return superMeta.does(role) return false }, getMethods : function () { return this.stem.properties.methods }, getAttributes : function () { return this.stem.properties.attributes }, afterMutate : function () { } }).c; Joose.Managed.Role = new Joose.Managed.Class('Joose.Managed.Role', { isa : Joose.Managed.Class, have : { defaultSuperClass : Joose.Proto.Empty, builderRole : null, stemRole : null }, methods : { defaultConstructor : function () { return function () { throw "Roles cant be instantiated" } }, processSuperClass : function () { if (this.superClass != this.defaultSuperClass) throw "Roles can't inherit from anything" }, getBuilderTarget : function () { if (!this.builderRole) { this.builderRole = new this.constructor().c this.builderClassCreated = true } return this.builderRole }, getStemTarget : function () { if (!this.stemRole) { this.stemRole = new this.constructor().c this.stemClassCreated = true } return this.stemRole }, addRequirement : function (methodName) { this.stem.properties.requirements.addProperty(methodName, {}) } }, stem : { methods : { apply : function () { }, unapply : function () { } } }, builder : { methods : { requires : function (targetClassMeta, info) { Joose.A.each(Joose.O.wantArray(info), function (methodName) { targetClassMeta.addRequirement(methodName) }, this) } } } }).c; Joose.Managed.Attribute = new Joose.Managed.Class('Joose.Managed.Attribute', { isa : Joose.Managed.Property.Attribute, have : { is : null, builder : null, isPrivate : false, role : null, publicName : null, setterName : null, getterName : null, //indicates the logical readableness/writeableness of the attribute readable : false, writeable : false, //indicates the physical presense of the accessor (may be absent for "combined" accessors for example) hasGetter : false, hasSetter : false, required : false }, after : { initialize : function () { var name = this.name this.publicName = name.replace(/^_+/, '') this.slot = this.isPrivate ? '$$' + name : name this.setterName = this.setterName || this.getSetterName() this.getterName = this.getterName || this.getGetterName() this.readable = this.hasGetter = /^r/i.test(this.is) this.writeable = this.hasSetter = /^.w/i.test(this.is) } }, override : { computeValue : function () { if (!Joose.O.isFunction(this.init)) this.SUPER() }, preApply : function (targetClass) { targetClass.meta.extend({ methods : this.getAccessorsFor(targetClass) }) }, postUnApply : function (from) { from.meta.extend({ removeMethods : this.getAccessorsFrom(from) }) } }, methods : { getAccessorsFor : function (targetClass) { var targetMeta = targetClass.meta var setterName = this.setterName var getterName = this.getterName var methods = {} if (this.hasSetter && !targetMeta.hasMethod(setterName)) { methods[setterName] = this.getSetter() methods[setterName].ACCESSOR_FROM = this } if (this.hasGetter && !targetMeta.hasMethod(getterName)) { methods[getterName] = this.getGetter() methods[getterName].ACCESSOR_FROM = this } return methods }, getAccessorsFrom : function (from) { var targetMeta = from.meta var setterName = this.setterName var getterName = this.getterName var setter = this.hasSetter && targetMeta.getMethod(setterName) var getter = this.hasGetter && targetMeta.getMethod(getterName) var removeMethods = [] if (setter && setter.value.ACCESSOR_FROM == this) removeMethods.push(setterName) if (getter && getter.value.ACCESSOR_FROM == this) removeMethods.push(getterName) return removeMethods }, getGetterName : function () { return 'get' + Joose.S.uppercaseFirst(this.publicName) }, getSetterName : function () { return 'set' + Joose.S.uppercaseFirst(this.publicName) }, getSetter : function () { var slot = this.slot return function (value) { this[slot] = value return this } }, getGetter : function () { var slot = this.slot return function () { return this[slot] } }, getValueFrom : function (instance) { var getterName = this.getterName if (this.readable && instance.meta.hasMethod(getterName)) return instance[getterName]() return instance[this.slot] }, setValueTo : function (instance, value) { var setterName = this.setterName if (this.writeable && instance.meta.hasMethod(setterName)) instance[setterName](value) else instance[this.slot] = value }, initFromConfig : function (instance, config) { var name = this.name var value, isSet = false if (config.hasOwnProperty(name)) { value = config[name] isSet = true } else if (Joose.O.isFunction(this.init)) { value = this.init.call(instance, name, config) isSet = true } else if (this.builder) { value = instance[ this.builder.replace(/^this\./, '') ](name, config) isSet = true } if (isSet) this.setValueTo(instance, value) else if (this.required) throw "Required attribute [" + name + "] is missed during initialization of " + instance } } }).c ; Joose.Managed.PropertySet.Namespace = new Joose.Proto.Class('Joose.Managed.PropertySet.Namespace', { isa : Joose.Managed.PropertySet, propertyMetaClass : null, targetMeta : null, container : null, initialize : function (props) { Joose.Managed.PropertySet.Namespace.superClass.initialize.call(this, props) this.container = this.targetMeta.c }, addProperty : function (name, value) { if (value && value.meta && value.meta.meta.hasAttribute('ns')) value.meta.parentNs = this.targetMeta.ns return this.container[name] = this.properties[name] = value }, removeProperty : function (name) { try { delete this.container[name] } catch(e) { this.container[name] = undefined } return Joose.Managed.PropertySet.Namespace.superClass.removeProperty.call(this, name) } }).c ; Joose.Managed.Attribute.Builder = new Joose.Managed.Role('Joose.Managed.Attribute.Builder', { have : { defaultAttributeClass : Joose.Managed.Attribute }, builder : { methods : { has : function (targetClassMeta, info) { Joose.O.eachOwn(info, function (props, name) { if (typeof props != 'object' || props == null || props.constructor == RegExp) props = { init : props } props.meta = props.meta || targetClassMeta.defaultAttributeClass if (/^__/.test(name)) { name = name.replace(/^_+/, '') props.isPrivate = true } targetClassMeta.addAttribute(name, props.init, props) }, this) }, hasnot : function (targetClassMeta, info) { this.havenot(targetClassMeta, info) }, hasnt : function (targetClassMeta, info) { this.hasnot(targetClassMeta, info) } } } }).c ; Joose.Managed.My = new Joose.Managed.Role('Joose.Managed.My', { have : { myClass : null, needToReAlias : false }, methods : { createMy : function (extend) { var thisMeta = this.meta var isRole = this instanceof Joose.Managed.Role var myExtend = extend.my || {} delete extend.my // Symbiont will generally have the same meta class as its hoster, excepting the cases, when the superclass also have the symbiont. // In such cases, the meta class for symbiont will be inherited (unless explicitly specified) if (!isRole) myExtend.isa = myExtend.isa || this.superClass.meta.myClass if (!myExtend.meta && !myExtend.isa) myExtend.meta = this.constructor var createdClass = this.myClass = Class(myExtend) this.c.prototype.my = this.c.my = isRole ? createdClass : new createdClass({ HOST : this.c }) this.needToReAlias = true }, aliasStaticMethods : function () { this.needToReAlias = false var c = this.c var myProto = this.myClass.prototype Joose.O.eachOwn(c, function (property, name) { if (property.IS_ALIAS) delete c[ name ] }) this.myClass.meta.stem.properties.methods.each(function (method, name) { if (!c[ name ]) (c[ name ] = function () { return myProto[ name ].apply(c.my, arguments) }).IS_ALIAS = true }) } }, override : { extend : function (props) { var myClass = this.myClass if (!myClass && this.superClass.meta.myClass) this.createMy(props) if (props.my) { if (!myClass) this.createMy(props) else { this.needToReAlias = true myClass.meta.extend(props.my) delete props.my } } this.SUPER(props) if (this.needToReAlias && !(this instanceof Joose.Managed.Role)) this.aliasStaticMethods() } }, before : { addRole : function () { var myStem Joose.A.each(arguments, function (arg) { //instanceof Class to allow treat classes as roles var role = (arg.meta instanceof Joose.Managed.Class) ? arg : arg.role if (role.meta.meta.hasAttribute('myClass') && role.meta.myClass) { if (!this.myClass) { this.createMy({ my : { does : role.meta.myClass } }) return } myStem = this.myClass.meta.stem if (!myStem.opened) myStem.open() myStem.addComposeInfo(role.my.meta.stem) } }, this) if (myStem) { myStem.close() this.needToReAlias = true } }, removeRole : function () { if (!this.myClass) return var myStem = this.myClass.meta.stem myStem.open() Joose.A.each(arguments, function (role) { if (role.meta.meta.hasAttribute('myClass') && role.meta.myClass) { myStem.removeComposeInfo(role.my.meta.stem) this.needToReAlias = true } }, this) myStem.close() } } }).c; Joose.Namespace = function () { throw "Modules may not be instantiated." } Joose.Namespace.Able = new Joose.Managed.Role('Joose.Namespace.Able', { have : { parentNs : null, ns : null, bodyFunc : null }, before : { extend : function (extend) { if (extend.body) { this.bodyFunc = extend.body delete extend.body } } }, after: { //at this point targetMeta will contain 'c' which is a container for Joose.Managed.PropertySet.Namespace adaptConstructor: function (extend) { var localName = (this.name || '').split('.').pop() //XXX check that 'ns' is overwritten after planting this.ns = new Joose.Managed.PropertySet.Namespace({ name : localName, targetMeta : this }) }, afterMutate : function () { var bodyFunc = this.bodyFunc delete this.bodyFunc if (bodyFunc) Joose.Namespace.Manager.my.executeIn(this.c, bodyFunc) } } }).c; Joose.Managed.Bootstrap = new Joose.Managed.Role('Joose.Managed.Bootstrap', { does : [ Joose.Namespace.Able, Joose.Managed.My, Joose.Managed.Attribute.Builder ] }).c ; Joose.Meta = function () { throw "Modules may not be instantiated." } Joose.Meta.Object = new Joose.Proto.Class('Joose.Meta.Object', { isa : Joose.Proto.Object }).c; Joose.Meta.Class = new Joose.Managed.Class('Joose.Meta.Class', { isa : Joose.Managed.Class, does : Joose.Managed.Bootstrap, have : { defaultSuperClass : Joose.Meta.Object } }).c ; Joose.Meta.Role = new Joose.Meta.Class('Joose.Meta.Role', { isa : Joose.Managed.Role, does : Joose.Managed.Bootstrap }).c; Joose.Namespace.Keeper = new Joose.Meta.Class('Joose.Namespace.Keeper', { isa : Joose.Meta.Class, have : { externalConstructor : null }, methods: { defaultConstructor: function () { return function () { //constructors should assume that meta is attached to 'arguments.callee' (not to 'this') var thisMeta = arguments.callee.meta if (thisMeta instanceof Joose.Namespace.Keeper) throw "Module [" + thisMeta.c + "] may not be instantiated." var externalConstructor = thisMeta.externalConstructor if (typeof externalConstructor == 'function') { externalConstructor.meta = thisMeta return externalConstructor.apply(this, arguments) } throw "NamespaceKeeper of [" + thisMeta.name + "] was planted incorrectly." } }, //withClass should be not constructed yet on this stage (see Joose.Proto.Class.construct) //it should be on the 'constructorOnly' life stage (should already have constructor) plant: function (withClass) { this.copyNamespaceState(withClass) var keeper = this.c keeper.meta = withClass.meta keeper.meta.c = keeper keeper.meta.externalConstructor = withClass }, copyNamespaceState : function (targetClass) { var targetMeta = targetClass.meta targetMeta.parentNs = this.parentNs targetMeta.ns = this.ns } } }).c ; Joose.Namespace.Manager = new Joose.Managed.Class('Joose.Namespace.Manager', { have : { global : null, globalNs : null, current : null }, methods : { initialize : function () { var globalKeeper = this.global = new Joose.Namespace.Keeper('').c var globalNs = this.globalNs = globalKeeper.meta.ns globalNs.container = Joose.is_NodeJS && global || Joose.top globalKeeper.meta.parentNs = globalKeeper this.current = [ globalKeeper ] }, getCurrent: function () { return this.current[0] }, executeIn : function (ns, func) { var current = this.current var scope = ns.meta.ns ? ns.meta.ns.container : ns current.unshift(ns) var res = func.call(scope, ns) current.shift() return res }, earlyCreate : function (name, metaClass, props) { props.constructorOnly = true return new metaClass(name, props).c }, //this function establishing the full "namespace chain" (including the last element) create : function (nsName, metaClass, extend) { //if no name provided, then we creating an anonymous class, so just skip all the namespace manipulations if (!nsName) return new metaClass(nsName, extend).c var me = this if (/^\./.test(nsName)) return this.executeIn(this.global, function () { return me.create(nsName.replace(/^\./, ''), metaClass, extend) }) props = extend || {} var parts = Joose.S.saneSplit(nsName, '.') var object = this.getCurrent() var soFar = Joose.S.saneSplit(object.meta.name, '.') for(var i = 0; i < parts.length; i++) { var part = parts[i] var isLast = i == parts.length - 1 if (part == "meta" || part == "my" || !part) throw "Module name [" + nsName + "] may not include a part called 'meta' or 'my' or empty part." var cur = (object == this.global ? this.global.meta.ns.container : object)[part]//object.meta.ns.getProperty(part) soFar.push(part) var soFarName = soFar.join(".") var needFinalize = false var nsKeeper if (typeof cur == "undefined") { if (isLast) { nsKeeper = this.earlyCreate(soFarName, metaClass, props) needFinalize = true } else nsKeeper = new Joose.Namespace.Keeper(soFarName).c if (object.meta) object.meta.ns.addProperty(part, nsKeeper) else object[part] = nsKeeper cur = nsKeeper } else if (isLast && cur && cur.meta) { //XXX needs cleanup and sanitizing if (cur.meta.constructor == metaClass && extend) cur.meta.extend(props) else if (cur.meta instanceof Joose.Namespace.Keeper && metaClass != Joose.Namespace.Keeper) { cur.meta.plant(this.earlyCreate(soFarName, metaClass, props)) needFinalize = true } else if (metaClass != Joose.Namespace.Keeper) throw "Re-declaration of class " + soFarName + "with different meta is not allowed" } else if (isLast && !(cur && cur.meta && cur.meta.meta && cur.meta.meta.hasAttribute('ns'))) throw "Trying to setup module " + soFarName + " failed. There is already something: " + cur if (needFinalize) cur.meta.construct(props) object = cur } return object }, // //this function establishing the full "namespace chain" (including the last element) // prepareNamespace : function (nsName) { // // var parts = Joose.S.saneSplit(nsName, '.') // var object = this.getCurrent() // var soFar = Joose.S.saneSplit(object.meta.name, '.') // // for(var i = 0; i < parts.length; i++) { // var part = parts[i] // // if (part == "meta" || part == "my" || !part) throw "Module name [" + nsName + "] may not include a part called 'meta' or 'my' or empty part." // // var cur = (object == this.global ? this.global.meta.ns.container : object)[part] // // soFar.push(part) // // if (cur === undefined) { // var nsKeeper = new Joose.Namespace.Keeper(soFar.join(".")).c // // var objectMeta = object.meta // // if (objectMeta && objectMeta.ns) // objectMeta.ns.addProperty(part, nsKeeper) // else // object[part] = nsKeeper // // cur = nsKeeper // } // // object = cur // } // // if (!(object && object.meta && object.meta.ns)) throw "Trying to setup module " + soFarName + " failed. There is already something: " + object // // return object // }, prepareProperties : function (name, props, defaultMeta, callback) { if (name && typeof name != 'string') { props = name name = null } var meta if (props && props.meta) { meta = props.meta delete props.meta } if (!meta) if (props && typeof props.isa == 'function') meta = props.isa.meta.constructor else meta = defaultMeta return callback.call(this, name, meta, props) }, getDefaultHelperFor : function (metaClass) { var me = this return function (name, props) { return me.prepareProperties(name, props, metaClass, function (name, meta, props) { return me.create(name, meta, props) }) } }, register : function (helperName, metaClass, func) { var me = this if (this.meta.hasMethod(helperName)) { var helper = function () { return me[helperName].apply(me, arguments) } if (!Joose.top[helperName]) Joose.top[helperName] = helper if (!Joose[helperName]) Joose[helperName] = helper // declaring NodeJS global if (Joose.is_NodeJS && !global[helperName]) global[helperName] = helper } else { var methods = {} methods[helperName] = func || this.getDefaultHelperFor(metaClass) this.meta.extend({ methods : methods }) this.register(helperName) } }, Module : function (name, props) { return this.prepareProperties(name, props, Joose.Namespace.Keeper, function (name, meta, props) { if (typeof props == 'function') props = { body : props } return this.create(name, meta, props) }) } } }).c Joose.Namespace.Manager.my = new Joose.Namespace.Manager() Joose.Namespace.Manager.my.register('Class', Joose.Meta.Class) Joose.Namespace.Manager.my.register('Role', Joose.Meta.Role) Joose.Namespace.Manager.my.register('Module');