UNPKG

siesta-lite

Version:

Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers

1,892 lines (1,188 loc) 721 kB
;if (typeof Joose == "undefined") !function () {; var Joose = {} // configuration hash Joose.C = typeof JOOSE_CFG != 'undefined' ? JOOSE_CFG : {} Joose.is_IE = '\v' == 'v' Joose.is_NodeJS = Boolean(typeof process != 'undefined' && process.pid) Joose.top = Joose.is_NodeJS && global || this Joose.stub = function () { return function () { throw new Error("Modules can not be instantiated") } } Joose.VERSION = ({ /*PKGVERSION*/VERSION : '3.50.1' }).VERSION if (typeof module != 'undefined') module.exports = Joose /*if (!Joose.is_NodeJS) */ this.Joose = Joose // 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 }, eachR : function (array, func, scope) { scope = scope || this for (var i = array.length - 1; i >= 0; 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) }, strToClass : function (name, top) { var current = top || Joose.top Joose.A.each(name.split('.'), function (segment) { if (current) current = current[ segment ] else return false }) return current } } var baseFunc = function () {} var enumProps = [ 'hasOwnProperty', 'valueOf', 'toString', 'constructor' ] var manualEnum = true for (var i in { toString : 1 }) manualEnum = false // 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 (manualEnum) return Joose.A.each(enumProps, 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) { baseFunc.prototype = object return new baseFunc() }, 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 }, isClass : function (obj) { return obj && obj.meta && obj.meta.c == obj }, wantArray : function (obj) { if (obj instanceof Array) return obj return [ obj ] }, // this was a bug in WebKit, which gives typeof / / == 'function' // should be monitored and removed at some point in the future isFunction : function (obj) { return typeof obj == 'function' && obj.constructor != / /.constructor } } //initializers Joose.I = { Array : function () { return [] }, Object : function () { return {} }, Function : function () { return arguments.callee }, Now : function () { return new Date() } }; Joose.Proto = Joose.stub() Joose.Proto.Empty = Joose.stub() Joose.Proto.Empty.meta = {}; ;(function () { Joose.Proto.Object = Joose.stub() 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) { if (extend.hasOwnProperty('isa') && !extend.isa) throw new Error("Attempt to inherit from undefined superclass [" + this.name + "]") var res = extend.isa || this.defaultSuperClass delete extend.isa return res }, processStem : function () { 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 (arg) { var BUILD = this.BUILD var args = BUILD && BUILD.apply(this, arguments) || arg || {} var thisMeta = this.meta thisMeta.initInstance(this, args) return thisMeta.hasMethod('initialize') && this.initialize(args) || this } }, processSuperClass: function (superClass) { var superProto = superClass.prototype //non-Joose superclasses if (!superClass.meta) { var extend = Joose.O.copy(superProto) extend.isa = Joose.Proto.Empty // clear potential value in the `extend.constructor` to prevent it from being modified delete extend.constructor var meta = new this.defaultSuperClass.meta.constructor(null, extend) superClass.meta = superProto.meta = meta meta.c = superClass } this.c.prototype = Joose.O.getMutableCopy(superProto) this.c.superClass = superProto }, 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 = Joose.stub() 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 new Error("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 new Error("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] }, clearValue : function (instance) { delete instance[ this.slot ] }, hasValue : function (instance) { return instance.hasOwnProperty(this.slot) }, getRawValueFrom : function (instance) { return instance[ this.slot ] }, setRawValueTo : function (instance, value) { instance[ this.slot ] = value return this } }).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 methodWrapper.__METHOD__ = this this.value.displayName = this.getDisplayName(target) methodWrapper.displayName = 'internal wrapper' targetProto[name] = methodWrapper }, getDisplayName : function (target) { return target.meta.name + '[' + this.name + ']' }, 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 }, getDisplayName : function (target) { return target.meta.name + '[override ' + this.name + ']' } }).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) }, getDisplayName : function (target) { return target.meta.name + '[' + this.name + ']' } }).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 } }, getDisplayName : function (target) { return target.meta.name + '[after ' + this.name + ']' } }).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) } }, getDisplayName : function (target) { return target.meta.name + '[before ' + this.name + ']' } }).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) } }, getDisplayName : function (target) { return target.meta.name + '[around ' + this.name + ']' } }).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 }, getDisplayName : function (target) { return target.meta.name + '[augment ' + this.name + ']' } }).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 (!targetProps.hasOwnProperty(name) || 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 ; 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) var properties = this.properties if (!properties[name]) properties[ name ] = [] properties[name].push(modifier) return modifier }, addPropertyObject : function (object) { var name = object.name var properties = this.properties if (!properties[name]) properties[name] = [] properties[name].push(object) return object }, //remove only the last modifier removeProperty : function (name) { if (!this.haveProperty(name)) return undefined var properties = this.properties var modifier = properties[ name ].pop() //if all modifiers were removed - clearing the properties if (!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) }) }) }, 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++ }) }, 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 var scope = scope || this Joose.A.each(this.processOrder, function (name) { func.call(scope, props[name], name) }) }, eachR : function (func, scope) { var props = this.properties var scope = scope || this Joose.A.eachR(this.processOrder, function (name) { func.call(scope, props[name], name) }) // 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 new Error("Unknown 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 }) }, name : function (targetMeta, name) { targetMeta.name = name } }).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