Joose = function () { throw "Modules may not be instantiated." }
Joose.top = this
Joose.VERSION = ({ /*VERSION*/VERSION : 3.012 }).VERSION
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 ]
},
// 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 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 () {
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
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 = 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]
},
clearValue : function (instance) {
delete instance[ this.slot ]
},
hasValue : function (instance) {
return instance.hasOwnProperty(this.slot)
},
getValueFrom : function (instance) {
return instance[ this.slot ]
},
setValueTo : 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 BUILD = this.BUILD
var props = BUILD && BUILD.apply(this, skipTraits ? params : arguments) || (skipTraits ? params : skipTraitsAnchor) || {}
// 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, config, name)
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 == / /.constructor) 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
//`Module` over something case
cur.meta.extend(props)
} 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' && props.isa.meta)
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');
;
Class("JooseX.SimpleRequest", {
have : {
req : null
},
methods: {
initialize: function () {
if (window.XMLHttpRequest)
this.req = new XMLHttpRequest()
else
this.req = new ActiveXObject("Microsoft.XMLHTTP")
},
getText: function (urlOrOptions, async, callback, scope) {
var req = this.req
var headers
var url
if (typeof urlOrOptions != 'string') {
headers = urlOrOptions.headers
url = urlOrOptions.url
async = async || urlOrOptions.async
callback = callback || urlOrOptions.callback
scope = scope || urlOrOptions.scope
} else url = urlOrOptions
req.open('GET', url, async || false)
if (headers) Joose.O.eachOwn(headers, function (value, name) {
req.setRequestHeader(name, value)
})
try {
req.onreadystatechange = function (event) {
if (async && req.readyState == 4) {
if (req.status == 200 || req.status == 0) callback.call(scope || this, true, req.responseText)
else callback.call(scope || this, false, "File not found: " + url)
}
};
req.send(null)
} catch (e) {
throw "File not found: " + url
}
if (!async)
if (req.status == 200 || req.status == 0) return req.responseText; else throw "File not found: " + url
return null
}
}
})
/**
Name
====
JooseX.SimpleRequest - Simple XHR request abstraction
SYNOPSIS
========
var req = new JooseX.SimpleRequest()
//asynchronous
req.getText('http://google.com', true, function (success, text) {
if (success) {
...
}
}, this)
//synchronous
try {
var text = req.getText('http://google.com')
} catch (e) {
}
//more options
req.getText({
url : 'http://google.com',
async : true,
callback : function (success, text) {
if (success) {
...
}
},
scope : this,
headers : {
'X-Custom-Header1' : 'Value',
'X-Custom-Header2' : 'Value'
}
})
DESCRIPTION
===========
`JooseX.SimpleRequest` is a *simple* XHR request abstraction (yet it works reasonably well across all major browsers).
It implements only 'GET' requests. If you need more features, you may want to look for another package.
METHODS
=======
### getText
> `void getText(String url, true, Function callback, [Object scope])`
> The signature above corresponds to asynchrnous usage. Method do not return the value and do not throws exception on error.
Instead, the success/error flag and returned text will be passed to `callback` functions, which will be executed in optional `scope`
> `String getText(String url)`
> The signature above corresponds to synchronous usage. Method returns the fetched text and throws exception on error.
> `void|String getText(Object params)`
> The signature above combine both variants of usage. The parameters for the call are passed as object with the following keys:
> - `url` - url to fetch text from
> - `async` - `true` for asynchrnous mode, `false` for synchronous
> - `callback` - the callback function. Will be called in asynchronous usage mode as callback(success, text)
> - `scope` - optional scope for `callback`
> - `headers` - object which key/value pairs will be used to initialize the headers of the request
SEE ALSO
========
Web page of this package: <http://github.com/SamuraiJack/JooseX-SimpleRequest/>
General documentation for Joose: <http://openjsan.org/go/?l=Joose>
BUGS
====
All complex software has bugs lurking in it, and this module is no exception.
Please report any bugs through the web interface at <http://github.com/SamuraiJack/JooseX-SimpleRequest/issues>
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.
[Test.Run.Result]: Result.html
[Test.Run.Harness]: Harness.html
GETTING HELP
============
This extension is supported via github issues tracker: <http://github.com/SamuraiJack/JooseX-SimpleRequest/issues>
For general Joose questions you can also visit #joose on irc.freenode.org or the forum at: [http://joose.it/forum](http://joose.it/forum)
SEE ALSO
========
Web page of this module: <http://github.com/SamuraiJack/JooseX-SimpleRequest/>
General documentation for Joose: <http://openjsan.org/go/?l=Joose>
BUGS
====
All complex software has bugs lurking in it, and this module is no exception.
Please report any bugs through the web interface at [http://github.com/SamuraiJack/JooseX-SimpleRequest/issues](http://github.com/SamuraiJack/JooseX-SimpleRequest/issues)
AUTHORS
=======
Malte Ubl
Nickolay Platonov [nplatonov@cpan.org](mailto: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.
*/
;
Class('JooseX.Namespace.Depended.Manager', {
my : {
have : {
INC : Joose.is_NodeJS ? require.paths : [ 'lib', '/jsan/index.html' ],
disableCaching : true,
resources : {},
resourceTypes : {},
ANONYMOUS_RESOURCE_COUNTER : 0
},
methods : {
//get own resource of some thing (resource will be also attached to that abstract thing)
//if the something is requesting own resource its considered loaded
getMyResource : function (type, token, me) {
var resource = this.getResource({
type : type,
token : token
})
if (resource.attachedTo && resource.attachedTo != me) throw resource + " is already attached to [" + resource.attachedTo + "]"
resource.attachedTo = me
resource.loaded = true
resource.loading = false
return resource
},
getResource : function (descriptor) {
if (typeof descriptor == 'object') {
var type = descriptor.type = descriptor.type || 'javascript'
var token = descriptor.token
var requiredVersion = descriptor.version
delete descriptor.version
} else
if (typeof descriptor == 'string') {
var match = /^(\w+):\/\/(.+)/.exec(descriptor)
if (match) {
// type & token are explicitly specified
type = match[1]
token = match[2]
if (type == 'http' || type == 'https') {
token = type + '/doc/s/sa/samuraijack/JooseX/CPS/012/lib/Task/JooseX/CPS//index.html' + token
type = 'javascript'
}
} else {
// no type specified
token = descriptor
type = /\//.test(token) || /\.js$/.test(token) ? 'javascript' : 'joose'
}
}
if (!token) {
token = '__ANONYMOUS_RESOURCE__' + this.ANONYMOUS_RESOURCE_COUNTER++
descriptor = undefined
}
var id = type + '/doc/s/sa/samuraijack/JooseX/CPS/012/lib/Task/JooseX/CPS//index.html' + token
var resource = this.resources[id]
if (!resource) {
var resourceClass = this.resourceTypes[type]
if (!resourceClass) throw "Unknown resource type: [" + type + "]"
resource = this.resources[id] = new resourceClass(typeof descriptor == 'object' ? descriptor : {
token : token,
type : type
})
}
resource.setRequiredVersion(requiredVersion)
return resource
},
registerResourceClass : function (typeName, resourceClass) {
this.resourceTypes[typeName] = resourceClass
},
use : function (dependenciesInfo, callback, scope) {
var nsManager = Joose.Namespace.Manager.my
var global = nsManager.global
Class({
use : dependenciesInfo,
body : function () {
if (callback) nsManager.executeIn(global, function (ns) {
callback.call(scope || this, ns)
})
}
})
}
}
}
})
use = function (dependenciesInfo, callback, scope) {
JooseX.Namespace.Depended.Manager.my.use(dependenciesInfo, callback, scope)
}
use.paths = JooseX.Namespace.Depended.Manager.my.INC
Joose.I.FutureClass = function (className) {
return function () {
return eval(className)
}
}
/**
Name
====
JooseX.Namespace.Depended.Manager - A global collection of all resources
SYNOPSIS
========
JooseX.Namespace.Depended.Manager.my.registerResourceClass('custom-type', JooseX.Namespace.Depended.Resource.Custom)
DESCRIPTION
===========
`JooseX.Namespace.Depended.Manager` is a global collection of all resources.
**Note:** Its a pure [static](/go/index_l_Joose.Manual.Static.html) class - all its methods and properties are static.
METHODS
=======
### registerResourceClass
> `void registerResourceClass(String type, Class constructor)`
> After you've created your custom resource class, you need to register it with call to this method.
> Then you can refer to new resources with the following descriptors:
{
type : 'custom-type',
token : 'some-token'
}
GETTING HELP
============
This extension is supported via github issues tracker: <http://github.com/SamuraiJack/JooseX-Namespace-Depended-Manager/issues>
For general Joose questions you can also visit #joose on irc.freenode.org or the forum at: [http://joose.it/forum](http://joose.it/forum)
SEE ALSO
========
Authoring [JooseX.Namespace.Depended](Authoring.html)
Abstract base resource class: [JooseX.Namespace.Depended.Resource](Resource.html)
General documentation for Joose: <http://openjsan.org/go/?l=Joose>
BUGS
====
All complex software has bugs lurking in it, and this module is no exception.
Please report any bugs through the web interface at [http://github.com/SamuraiJack/JooseX-Namespace-Depended-Manager/issues](http://github.com/SamuraiJack/JooseX-Namespace-Depended-Manager/issues)
AUTHORS
=======
Nickolay Platonov [nplatonov@cpan.org](mailto: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.
*/
;
Class('JooseX.Namespace.Depended.Resource', {
has : {
attachedTo : null,
type : null,
token : null,
id : null,
loading : false,
loaded : false,
ready : false,
presence : null,
readyness : null,
loadedFromURL : null,
readyListeners : Joose.I.Array,
dependencies : Joose.I.Object,
onBeforeReady : { is : 'rw', init : null },
readyDelegated : false,
version : { is : 'rw', init : null },
requiredVersion : { is : 'rw', init : null },
hasReadyCheckScheduled : false
},
after: {
initialize: function () {
if (!this.id) this.id = this.type + '/doc/s/sa/samuraijack/JooseX/CPS/012/lib/Task/JooseX/CPS//index.html' + this.token
}
},
methods: {
setOnBeforeReady : function (func) {
if (this.onBeforeReady) throw "Can't redefine 'onBeforeReady' for " + this
this.onBeforeReady = func
},
setVersion : function (version) {
if (!version) return
if (this.version && this.version != version) throw "Cant redefine version of " + this
var requiredVersion = this.requiredVersion
if (requiredVersion && version < requiredVersion) throw "Versions conflict on " + this + " required [" + requiredVersion + "], got [" + version + "]"
this.version = version
},
setRequiredVersion : function (version) {
if (!version) return
var requiredVersion = this.requiredVersion
if (!requiredVersion || version > requiredVersion)
if (this.isLoaded() || this.loading)
throw "Cant increase required version - " + this + " is already loaded"
else
this.requiredVersion = version
},
toString : function () {
return "Resource: id=[" + this.id + "], type=[" + this.meta.name + "]"
},
addDescriptor : function (descriptor) {
var resource = JooseX.Namespace.Depended.Manager.my.getResource(descriptor)
var dependencies = this.dependencies
var resourceID = resource.id
//if there is already such dependency or the resource is ready
if (dependencies[ resourceID ] || resource.isReady()) return
var me = this
//pushing listener to the end(!) of the list
resource.readyListeners.push(function () {
delete dependencies[ resourceID ]
me.checkReady()
})
//adding dependency
dependencies[ resourceID ] = resource
//we are not ready, since there are depedencies to load
this.ready = false
},
handleDependencies : function () {
// || {} required for classes on which this Role was applied after they were created - they have this.dependencies not initialized
Joose.O.eachOwn(this.dependencies || {}, function (resource) {
resource.handleLoad()
})
this.checkReady()
},
checkReady : function () {
if (!Joose.O.isEmpty(this.dependencies) || this.hasReadyCheckScheduled) return
if (this.onBeforeReady) {
if (!this.readyDelegated) {
this.readyDelegated = true
var me = this
this.onBeforeReady(function(){
me.fireReady()
}, me)
}
} else
this.fireReady()
},
fireReady: function () {
this.ready = true
var listeners = this.readyListeners
this.readyListeners = []
Joose.A.each(listeners, function (listener) {
listener()
})
},
isReady : function () {
if (!this.isLoaded()) return false
var isReady = false
try {
isReady = this.readyness()
} catch (e) {
}
return isReady || this.ready
},
isLoaded : function () {
var isPresent = false
try {
isPresent = this.presence()
} catch (e) {
}
return isPresent || this.loaded
},
handleLoad: function() {
if (this.isLoaded()) {
this.checkReady()
return
}
if (this.loading) return
this.loading = true
var urls = Joose.O.wantArray(this.getUrls())
var me = this
// this delays the 'checkReady' until the resourse will be *fully* materialized
// *fully* means that even the main class of the resource is already "ready"
// the possible other classes in the same file could be not
// see 110_several_classes_in_file.t.js, 120_script_tag_transport.t.js for example
me.hasReadyCheckScheduled = true
var onsuccess = function (resourceBlob, url) {
me.loaded = true
me.loading = false
me.loadedFromURL = url
me.materialize(resourceBlob, url)
me.hasReadyCheckScheduled = false
me.checkReady()
}
var onerror = function (e) {
//if no more urls
if (!urls.length) throw me + " not found"
me.load(urls.shift(), onsuccess, onerror)
}
this.load(urls.shift(), onsuccess, onerror)
},
getUrls: function () {
throw "Abstract resource method 'getUrls' was called"
},
load : function (url, onsuccess, onerror) {
throw "Abstract resource method 'load' was called"
},
materialize : function (resourceBlob) {
throw "Abstract resource method 'materialize' was called"
}
}
})
/**
Name
====
JooseX.Namespace.Depended.Resource - Abstract resource class
SYNOPSIS
========
//mostly for subclassing only
Class("JooseX.Namespace.Depended.Resource.JavaScript", {
isa : JooseX.Namespace.Depended.Resource,
...
})
DESCRIPTION
===========
`JooseX.Namespace.Depended.Resource` is an abstract resource class. Its not supposed to be used directly, instead you should use
one of its subclasses.
ATTRIBUTES
==========
### attachedTo
> `Object attachedTo`
> An arbitrary object to which this resource is attached (its a corresponding class in JooseX.Namespace.Depended)
### type
> `String type`
> A type of resource - plain string. `JooseX.Namespace.Depended.Manager` maintain a collection of resource types, accessible
### token
> `String token`
> A token of resource - plain string with arbitrary semantic. Each subclass should provide this semantic along with `token -> url` conertion method (locator)
### id
> `String id`
> An id of resource - is computed as `type + '/doc/s/sa/samuraijack/JooseX/CPS/012/lib/Task/JooseX/CPS//index.html' + token'
### loading
> `Boolean loading`
> A sign whether this resource is currently loading
### loaded
> `Boolean loaded`
> A sign whether this resource is already loaded
### ready
> `Boolean ready`
> A sign whether this resource is considered ready. Resource is ready, when its loaded, and all its dependencies are ready.
### loadedFromURL
> `String loadedFromURL`
> An url, from which the resource was loaded.
### readyListeners
> `Array[Function] readyListeners`
> An array of functions, which will be called after this resource becomes ready. Functions will be called sequentially.
### dependencies
> `Object dependencies`
> An object containing the dependencies of this resource. Keys are the `id`s of resources and the values - the resource instances itself.
### onBeforeReady
> `Function onBeforeReady`
> A function, which will be called, right after the all dependencies of the resource became ready, but before its own `readyListeners` will be called.
It supposed to perform any needed additional actions to post-process the loaded resource.
> Function will receive two arguments - the 1st is the callback, which should be called when `onBeforeReady` will finish its work. 2nd is the resource instance.
### version
> `r/w Number version`
> A version of this resource. Currently is handled as Number, this may change in future releases.
### requiredVersion
> `r/w Number requiredVersion`
> A *requiredVersion* version of this resource. Required here means the maximum version from all references to this resource.
METHODS
=======
### addDescriptor
> `void addDescriptor(Object|String descriptor)`
> Add the resource, described with passed descriptor as the dependency for this resource.
### getUrls
> `String|Array[String] getUrls()`
> Abstract method, will throw an exception if not overriden. It should return the array of urls (or a single url) from which this resource can be potentially loaded.
This method should take into account the `JooseX.Namespace.Depended.Manager.my.INC` setting
### load
> `void load(String url, Function onsuccess, Function onerror)`
> Abstract method, will throw an exception if not overriden. It should load the content of the resource from the passed `url`. If there was an error during loading
(for example file not found) should not throw the exception. Instead, should call the `onerror` continuation with it (exception instance).
> After successfull loading, should call the `onsuccess` continuation with the resource content as 1st argument, and `url` as 2nd: `onsuccess(text, url)`
### materialize
> `void materialize(String resourceBlob, String url)`
> Abstract method, will throw an exception if not overriden. It should "materialize" the resource. The concrete semantic of this action is determined by resource nature.
For example this method can create some tag in the DOM tree, or execute the code or something else.
> Currently this method is supposed to operate synchronously, this may change in future releases.
SEE ALSO
========
Web page of this package: <http://github.com/SamuraiJack/JooseX-Namespace-Depended-Resource/>
General documentation for Joose: <http://openjsan.org/go/?l=Joose>
BUGS
====
All complex software has bugs lurking in it, and this module is no exception.
Please report any bugs through the web interface at <http://github.com/SamuraiJack/JooseX-Namespace-Depended-Resource/issues>
AUTHORS
=======
Nickolay Platonov <nplatonov@cpan.org>
COPYRIGHT AND LICENSE
=====================
Copyright (c) 2009-2010, 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.
*/
;
Role('JooseX.Namespace.Depended.Materialize.Eval', {
requires : [ 'handleLoad' ],
methods : {
materialize : function (resourceBlob) {
;(function (){
eval(resourceBlob)
})()
}
}
})
/**
Name
====
JooseX.Namespace.Depended.Materialize.Eval - materializator, which treat the resource content as JavaScript code, and use `eval` function to evalute it
SYNOPSIS
========
//generally for consuming only
Class("JooseX.Namespace.Depended.Resource.Custom", {
isa : JooseX.Namespace.Depended.Resource,
does : [ JooseX.Namespace.Depended.Materialize.Eval, ...]
...
})
DESCRIPTION
===========
`JooseX.Namespace.Depended.Materialize.Eval` is a materializator role. It provide the implementation of `materialize` method.
SEE ALSO
========
Authoring [JooseX.Namespace.Depended](../Authoring.html)
Abstract base resource class: [JooseX.Namespace.Depended.Resource](../Resource.html)
General documentation for Joose: <http://openjsan.org/go/?l=Joose>
AUTHORS
=======
Nickolay Platonov <nplatonov@cpan.org>
COPYRIGHT AND LICENSE
=====================
Copyright (c) 2009-2010, 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.
*/;
Class('JooseX.Namespace.Depended.Resource.JavaScript', {
isa : JooseX.Namespace.Depended.Resource,
has : {
hasDirectUrl : false
},
after: {
initialize: function () {
var me = this
if (this.type == 'nonjoose') this.type = 'javascript'
var presence = this.presence
if (typeof presence == 'string') this.presence = function () {
return eval(presence)
}
if (!presence) this.presence = function () {
return eval(me.token)
}
if (!this.readyness) this.readyness = this.presence
}
},
methods : {
BUILD : function (config) {
var token = config.token
var match = /^=(.*)/.exec(token)
if (match) {
this.hasDirectUrl = true
token = match[1]
}
if (/^http/.test(token)) {
this.hasDirectUrl = true
config.trait = JooseX.Namespace.Depended.Transport.ScriptTag
}
if (/^\//.test(token)) this.hasDirectUrl = true
return config
},
getUrls : function () {
var url = this.token
if (this.hasDirectUrl) return [ url ]
var manager = JooseX.Namespace.Depended.Manager.my
return Joose.A.map(manager.INC, function (libroot) {
libroot = libroot.replace(/\/$/, '')
return [ libroot ].concat(url).join('/index.html') + (manager.disableCaching ? '?disableCaching=' + new Date().getTime() : '')
})
}
}
})
JooseX.Namespace.Depended.Manager.my.registerResourceClass('javascript', JooseX.Namespace.Depended.Resource.JavaScript)
JooseX.Namespace.Depended.Manager.my.registerResourceClass('nonjoose', JooseX.Namespace.Depended.Resource.JavaScript)
;
Class('JooseX.Namespace.Depended.Resource.JooseClass', {
isa : JooseX.Namespace.Depended.Resource.JavaScript,
// NOTE : we don't add the default materialization and transport roles here - they'll be added
// in one of the Bootstrap/*.js files
after: {
initialize: function () {
var me = this
this.presence = function () {
var c = eval(me.token)
return c && c.meta.resource
}
this.readyness = function () {
var c = eval(me.token)
return c && c.meta.resource.ready
}
}
},
methods : {
addDescriptor : function (descriptor) {
if (typeof descriptor == 'object' && !descriptor.token)
Joose.O.eachOwn(descriptor, function (version, name) {
this.addDescriptor({
type : 'joose',
token : name,
version : version
})
}, this)
else
this.SUPER(descriptor)
},
getUrls : function () {
var urls = []
var className = this.token.split('.')
var manager = JooseX.Namespace.Depended.Manager.my
return Joose.A.map(manager.INC, function (libroot) {
libroot = libroot.replace(/\/$/, '')
return [ libroot ].concat(className).join('/index.html') + '/doc/s/sa/samuraijack/JooseX/CPS/012/lib/Task/JooseX/CPS/.js' + (manager.disableCaching ? '?disableCaching=' + new Date().getTime() : '')
})
}
}
})
JooseX.Namespace.Depended.Manager.my.registerResourceClass('joose', JooseX.Namespace.Depended.Resource.JooseClass);
Role('JooseX.Namespace.Depended.Materialize.ScriptTag', {
requires : [ 'handleLoad' ],
methods : {
materialize : function (resourceBlob) {
var loaderNode = document.createElement("script")
loaderNode.text = resourceBlob
//adding to body, because Safari do not create HEAD for iframe's documents
document.body.appendChild(loaderNode)
}
}
})
;
Role('JooseX.Namespace.Depended.Transport.XHRAsync', {
requires : [ 'handleLoad' ],
override : {
load: function (url, onsuccess, onerror) {
var req = new JooseX.SimpleRequest()
try {
req.getText(url, true, function (success, text) {
if (!success) {
onerror(this + " not found")
return
}
onsuccess(text, url)
})
} catch (e) {
onerror(e)
}
}
}
})
/**
Name
====
JooseX.Namespace.Depended.Transport.XHRAsync - transport, which use the asynchronous XHR request for resource loading
SYNOPSIS
========
//generally for consuming only
Class("JooseX.Namespace.Depended.Resource.Custom", {
isa : JooseX.Namespace.Depended.Resource,
does : [ JooseX.Namespace.Depended.Transport.XHRAsync, ...]
...
})
DESCRIPTION
===========
`JooseX.Namespace.Depended.Transport.XHRAsync` is a transport role. It provide the implementation of `load` method, which use the
asynchronous XHR request for resource loading.
SEE ALSO
========
Authoring [JooseX.Namespace.Depended](../Authoring.html)
Abstract base resource class: [JooseX.Namespace.Depended.Resource](../Resource.html)
General documentation for Joose: <http://openjsan.org/go/?l=Joose>
AUTHORS
=======
Nickolay Platonov <nplatonov@cpan.org>
COPYRIGHT AND LICENSE
=====================
Copyright (c) 2009-2010, 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.
*/;
Role('JooseX.Namespace.Depended.Transport.XHRSync', {
requires : [ 'handleLoad' ],
override : {
load: function (url, onsuccess, onerror) {
var req = new JooseX.SimpleRequest()
var text;
try {
text = req.getText(url)
} catch (e) {
onerror(e)
return
}
onsuccess(text, url)
}
}
})
/**
Name
====
JooseX.Namespace.Depended.Transport.XHRSync - transport, which use the synchronous XHR request for resource loading
SYNOPSIS
========
//generally for consuming only
Class("JooseX.Namespace.Depended.Resource.Custom", {
isa : JooseX.Namespace.Depended.Resource,
does : [ JooseX.Namespace.Depended.Transport.XHRSync, ...]
...
})
DESCRIPTION
===========
`JooseX.Namespace.Depended.Transport.XHRSync` is a transport role. It provide the implementation of `load` method, which use the
synchronous XHR request for resource loading.
SEE ALSO
========
Authoring [JooseX.Namespace.Depended](../Authoring.html)
Abstract base resource class: [JooseX.Namespace.Depended.Resource](../Resource.html)
General documentation for Joose: <http://openjsan.org/go/?l=Joose>
AUTHORS
=======
Nickolay Platonov <nplatonov@cpan.org>
COPYRIGHT AND LICENSE
=====================
Copyright (c) 2009-2010, 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.
*/;
Role('JooseX.Namespace.Depended.Transport.ScriptTag', {
requires : [ 'handleLoad' ],
override : {
load: function (url, onsuccess, onerror) {
var loaderNode = document.createElement("script")
// if (Joose.is_IE) var errorTimeOut
loaderNode.onload = loaderNode.onreadystatechange = function () {
if (!loaderNode.readyState || loaderNode.readyState == "loaded" || loaderNode.readyState == "complete" || loaderNode.readyState == 4 && loaderNode.status == 200)
//surely for IE6..
setTimeout(function () { onsuccess(loaderNode.text, url) }, 1)
}
loaderNode.setAttribute("type", "text/javascript")
loaderNode.setAttribute("src", url)
document.getElementsByTagName("head")[0].appendChild(loaderNode)
},
materialize : function () {
}
}
})
/**
Name
====
JooseX.Namespace.Depended.Transport.ScriptTag - transport, which use the <script> tag for resource loading
SYNOPSIS
========
//generally for consuming only
Class("JooseX.Namespace.Depended.Resource.Custom", {
isa : JooseX.Namespace.Depended.Resource,
does : [ JooseX.Namespace.Depended.Transport.ScriptTag, ...]
...
})
DESCRIPTION
===========
`JooseX.Namespace.Depended.Transport.ScriptTag` is a transport role. It provide the implementation of `load` method, which use the
<script> tag for resource loading. It also overrides the `materialize` method as <script> tag execute the code along with loading.
SEE ALSO
========
Authoring [JooseX.Namespace.Depended](../Authoring.html)
Abstract base resource class: [JooseX.Namespace.Depended.Resource](../Resource.html)
General documentation for Joose: <http://openjsan.org/go/?l=Joose>
AUTHORS
=======
Nickolay Platonov <nplatonov@cpan.org>
COPYRIGHT AND LICENSE
=====================
Copyright (c) 2009-2010, 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.
*/;
Role('JooseX.Namespace.Depended', {
/*VERSION*/VERSION : 0.09,
meta : Joose.Managed.Role,
requires : [ 'prepareProperties' ],
have : {
containResources : [ 'use', 'meta', 'isa', 'does', 'trait', 'traits' ]
},
override: {
prepareProperties : function (name, extend, defaultMeta, callback) {
if (name && typeof name != 'string') {
extend = name
name = null
}
extend = extend || {}
var summaredDeps = []
var extendMy = extend.my
//gathering all the related resourses from various builders
//also gathering resourses of 'my'
Joose.A.each(this.containResources, function (propName) {
this.collectDependencies(extend[propName], summaredDeps, extend, propName)
if (extendMy && extendMy[propName]) this.collectDependencies(extendMy[propName], summaredDeps, extendMy, propName)
}, this)
//and from externally collected additional resources
this.alsoDependsFrom(extend, summaredDeps)
var resource = JooseX.Namespace.Depended.Manager.my.getResource({
type : 'joose',
token : name
})
if (extend.VERSION) resource.setVersion(extend.VERSION)
//BEGIN executes right after the all dependencies are loaded, but before this module becomes ready (before body())
//this allows to manually control the "ready-ness" of module (custom pre-processing)
//BEGIN receives the function (callback), which should be called at the end of custom processing
if (extend.BEGIN) {
resource.setOnBeforeReady(extend.BEGIN)
delete extend.BEGIN
}
Joose.A.each(summaredDeps, function (descriptor) {
resource.addDescriptor(descriptor)
})
//skip constructing for classes w/o dependencies
if (Joose.O.isEmpty(resource.dependencies)) {
this.inlineDependencies(extend)
var res = this.SUPER(name, extend, defaultMeta, callback)
//this will allow to classes which don't have dependencies to be ready synchronously
resource.checkReady()
return res
} else {
// defer the dependencies loading, because they actually could be provided later in the same bundle file
// this, however, affect performance, so bundles should be created in the dependencies-ordered way
setTimeout(function () {
resource.handleDependencies()
}, 0)
// debugging warning
if (typeof ENABLE_DEFERRED_DEPS_WARNING != 'undefined')
console.log('Deferred deps handling for class [' + name + '], deps = [' + JSON.stringify(resource.dependencies) + ']')
}
var me = this
//unshift is critical for correct order of readyListerens processing!
//constructing is delaying until resource will become ready
resource.readyListeners.unshift(function () {
me.inlineDependencies(extend)
me.prepareProperties(name, extend, defaultMeta, callback)
})
return this.create(name, Joose.Namespace.Keeper, {})
},
create : function () {
var meta = this.SUPERARG(arguments).meta
meta.resource = meta.resource || JooseX.Namespace.Depended.Manager.my.getMyResource('joose', meta.name, meta.c)
return meta.c
}
},
//eof override
methods : {
alsoDependsFrom : function (extend, summaredDeps) {
},
collectDependencies : function (from, to, extend, propName) {
Joose.A.each(Joose.O.wantArray(from), function (descriptor) {
if (descriptor && typeof descriptor != 'function') to.push(descriptor)
})
},
inlineDependencies : function (extend) {
this.inlineDeps(extend)
var extendMy = extend.my
if (extendMy) this.inlineDeps(extendMy)
},
inlineDeps : function (extend) {
delete extend.use
Joose.A.each(this.containResources, function (propName) {
if (extend[propName]) {
var descriptors = []
Joose.A.each(Joose.O.wantArray(extend[propName]), function (descriptor, index) {
var descType = typeof descriptor
if (descType == 'function')
descriptors.push(descriptor.meta ? descriptor : descriptor())
else
if (descType == 'object')
if (descriptor.token)
descriptors.push(eval(descriptor.token))
else
Joose.O.each(descriptor, function (version, name) {
descriptors.push(eval(name))
})
else
if (descType == 'string')
descriptors.push(eval(descriptor))
else
throw "Wrong dependency descriptor format: " + descriptor
})
if (propName != 'isa' && propName != 'meta')
extend[propName] = descriptors
else
if (descriptors.length > 1)
throw "Cant specify several super- or meta- classes"
else
extend[propName] = descriptors[0]
}
})
}
}
})
Joose.Namespace.Manager.meta.extend({
does : JooseX.Namespace.Depended
})
Joose.Namespace.Keeper.meta.extend({
after: {
copyNamespaceState: function (targetClass) {
targetClass.meta.resource = this.resource
}
}
})
;
JooseX.Namespace.Depended.Resource.JavaScript.meta.extend({
does : [ JooseX.Namespace.Depended.Transport.XHRAsync, JooseX.Namespace.Depended.Materialize.Eval ]
})
;
;
Class('JooseX.CPS.Continuation', {
has : {
parent : null,
previous : null,
statements : Joose.I.Array,
type : 'Sequential',
entered : false,
leaved : false,
defaultScope : null,
nextFunc : null,
// re-entry func is analog of "nextFunc" for inter-statements control flow tranfers into single continuation (sequential runcore)
reEntryFunc : null,
catchFunc : null,
catchScope : null,
finallyFunc : null,
finallyScope : null
},
methods : {
deriveChild : function (config) {
config = config || {}
config.parent = this
return new this.constructor(config)
},
deriveSibling : function (config) {
config = config || {}
config.parent = this.parent
config.previous = this
return new this.constructor(config)
},
getNextFunc : function (doNotReEntry) {
var parent = this.parent
return !doNotReEntry && this.reEntryFunc || this.nextFunc || parent && parent.getNextFunc()
},
getScope : function () {
var prev = this.previous || this.parent
return this.defaultScope || prev && prev.getScope() || Joose.top
},
entry : function () {
if (this.entered) throw "Can't re-enter the continuation + [" + this + "]"
this.entered = true
this[ 'runCore' + this.type ].apply(this, arguments)
},
runCoreSequential : function () {
var me = this
var statements = this.statements
if (statements.length) {
var statement = statements.shift()
var reEntryFunc = function () {
me.runCoreSequential.apply(me, arguments)
}
if (statements.length)
this.reEntryFunc = reEntryFunc
else
delete this.reEntryFunc
var child = this.deriveChild({
defaultScope : statement.scope,
nextFunc : reEntryFunc
})
this.run(statement.func, statement.scope, statement.args || arguments, child)
} else {
delete this.reEntryFunc
this.leave.apply(this, arguments)
}
},
runCoreParallel : function () {
var args = arguments
var statements = this.statements
var length = statements.length
var me = this
var results = []
var counter = 0
Joose.A.each(statements, function (statement, index) {
TRY(statement.func, statement.scope, statement.args || args).THEN(function () {
results[ index ] = arguments
this.CONT.CONTINUE()
}).CATCH(function () {
results[ index ] = arguments
this.CONT.CONTINUE()
}).FINALLY(function () {
counter++
if (counter == length) me.leave.apply(me, results)
}).NOW()
})
},
run : function (func, scope, args, statement) {
var glob = Joose.top
var prevScopeStatement = scope.CONT
var prevGlobStatement = glob.__GLOBAL_CNT__
glob.__GLOBAL_CNT__ = scope.CONT = statement
try {
if (func.apply(scope, args) !== undefined) throw "ERROR: Value returned from continued function (use `CONTINUE(value)` instead)"
} catch (e) {
// if statement is already leaved, then we are just propagating the exception from the further statements
if (statement.leaved) throw e
statement.THROW(e)
} finally {
scope.CONT = prevScopeStatement
glob.__GLOBAL_CNT__ = prevGlobStatement
}
},
leave : function () {
var args = arguments
var finallyFunc = this.finallyFunc
if (finallyFunc) {
delete this.finallyFunc
var finallyScope = this.finallyScope
var me = this
var finallyStatement = this.deriveChild({
defaultScope : finallyScope,
nextFunc : function () {
me.leave.apply(me, args)
}
})
this.run(finallyFunc, finallyScope, [], finallyStatement)
return
}
if (this.leaved) throw "Can't re-leave the continuation + [" + this + "]"
this.leaved = true
var nextFunc = this.getNextFunc()
if (nextFunc) nextFunc.apply(Joose.top, args)
},
THROW : function (exception) {
var args = arguments
// remove the `reEntryFunc` (if any) as we aren't supposed to return the sequential flow after THROW
delete this.reEntryFunc
var catchFunc = this.catchFunc
if (catchFunc) {
delete this.catchFunc
var me = this
var catchScope = this.catchScope
var catchStatement = this.deriveChild({
defaultScope : catchScope,
nextFunc : function () {
me.leave.apply(me, arguments)
}
})
this.run(catchFunc, catchScope, args, catchStatement)
return
}
var parent = this.parent
if (parent) {
this.nextFunc = function () {
parent.THROW.apply(parent, args)
}
this.leave()
} else {
this.nextFunc = function () {
throw exception
}
this.leave()
}
},
CONTINUE : function () {
return this.leave.apply(this, arguments)
},
RETURN : function () {
if (this.parent) this.nextFunc = this.parent.getNextFunc(true)
return this.leave.apply(this, arguments)
},
TRY : function (func, scope, args) {
if (this.leaved) throw "Can't call 'TRY' for [" + this + "] - its already leaved"
if (!func) throw "Invalid parameters for 'TRY' in [" + this + "]"
if (this.catchFunc || this.finallyFunc) return this.NEXT.apply(this, arguments)
this.statements.push({
func : func,
scope : scope || this.getScope(),
args : args
})
this.defaultScope = scope || this.defaultScope
return this
},
THEN : function () {
if (this.type == 'Parallel') return this.NEXT.apply(this, arguments)
return this.TRY.apply(this, arguments)
},
andTHEN : function () {
return this.THEN.apply(this, arguments).now()
},
CATCH : function (func, scope) {
if (this.leaved) throw "Can't call 'CATCH' for [" + this + "] - its already leaved"
if (!func) throw "Invalid parameters for 'CATCH' in [" + this + "]"
if (this.catchFunc) throw "Can't redefine 'CATCH' for [" + this + "]"
this.catchFunc = func
this.catchScope = scope || this.getScope()
return this
},
FINALLY : function (func, scope) {
if (this.leaved) throw "Can't call 'FINALLY' for [" + this + "] - its already leaved"
if (!func) throw "Invalid parameters for 'FINALLY' in [" + this + "]"
if (this.finallyFunc) throw "Can't redefine 'FINALLY' for [" + this + "]"
this.finallyFunc = func
this.finallyScope = scope || this.getScope()
return this
},
NEXT : function (func, scope, args) {
if (this.leaved) throw "Can't call 'NEXT' for [" + this + "] - its already leaved"
var next = this.deriveSibling()
this.nextFunc = function () {
next.entry.apply(next, arguments)
}
return next.TRY(func, scope, args)
},
AND : function () {
this.type = 'Parallel'
return this.TRY.apply(this, arguments)
},
NOW : function () {
var root = this.getNearestNotEntered()
if (!root) throw "Can't launch [" + this + "]"
root.entry.apply(root, arguments)
},
getNearestNotEntered : function () {
if (this.entered) return null
var prev = this.previous || this.parent
if (prev) {
var root = prev.getNearestNotEntered()
if (root) return root
}
return this
},
// Delegates
getCONTINUE : function () {
var me = this
return function () {
me.CONTINUE.apply(me, arguments)
}
},
getRETURN : function () {
var me = this
return function () {
me.RETURN.apply(me, arguments)
}
},
getTHROW : function () {
var me = this
return function () {
me.THROW.apply(me, arguments)
}
},
// Synonyms
and : function () {
return this.AND.apply(this, arguments)
},
then : function () {
return this.THEN.apply(this, arguments)
},
andThen : function () {
return this.andTHEN.apply(this, arguments)
},
next : function () {
return this.NEXT.apply(this, arguments)
},
now : function () {
return this.NOW.apply(this, arguments)
},
except : function () {
return this.CATCH.apply(this, arguments)
},
ensure : function () {
return this.FINALLY.apply(this, arguments)
}
}
//eof methods
})
TRY = function () {
var continuation = new JooseX.CPS.Continuation()
return continuation.TRY.apply(continuation, arguments)
}
/**
Name
====
JooseX.CPS.Continuation - A continuation class
SYNOPSIS
========
TRY = function () {
var continuation = new JooseX.CPS.Continuation()
return continuation.TRY.apply(continuation, arguments)
}
DESCRIPTION
===========
`JooseX.CPS.Continuation` implements a continuation - an underlaying basis for `JooseX.CPS` trait.
ISA
===
None.
DOES
====
None.
TRAITS
======
None.
ATTRIBUTES
==========
### parent
> `JooseX.CPS.Continuation parent`
> A parent for this continuation. Can be asked for default scope or for the [nextFunc]
### previous
> `JooseX.CPS.Continuation previous`
> A previous continuation for this continuation. Can be asked for default scope or for the [nextFunc]
### statements
> `Array statements`
> An array of statements. Each statement is an object like :
{
func : ... , // function to execute
scope : ... , // scope into which execute the function
args : ... // arguments for function
}
### type
> `String type`
> The type of this continuation. Can be 'Sequential' or 'Parallel's
### entered
> `Boolean entered`
> The sign whether this continuation was already entered - i.e. activated.
### leaved
> `Boolean leaved`
> The sign whether this continuation was already leaved - i.e. the `CONTINUE` or `THROW` method were called.
### defaultScope
> `Object defaultScope`
> The default scope which will be supplied to the statements if not provided explicitly. Once passed to `TRY`, propagates to the further statements.
### nextFunc
> `Function nextFunc`
> If present, this function will be called, when leaving this continuation. Will be called in the global scope, with the arguments from the method, initated the leave.
### catchFunc/finallyFunc
> `Function catchFunc/finallyFunc`
> The functions for `CATCH/FINALLY` statements accordingly.
### catchScope/finallyScope
> `Object catchScope/finallyScope`
> The scopes for `CATCH/FINALLY` statements accordingly.
METHODS
=======
### TRY
> `JooseX.CPS.Continuation TRY(Function func, Object scope?, Array args?)`
> Add a statement to the current continuation. If continuation already contains `CATCH` or `FINALLY` statements - then delegate to `NEXT` and return a next continuation instance.
otherwise return current continuation.
### THEN
> `JooseX.CPS.Continuation THEN(Function func, Object scope?, Array args?)`
> Alias for `TRY` with a single exception. If the type of the continuation is `Parallel` then delegate to `NEXT` and return a next continuation instance.
> Has a lower-case synonym : 'then'
### CATCH
> `JooseX.CPS.Continuation CATCH(Function func, Object scope?)`
> Add a `CATCH` statement to the current continuation.
> Has a lower-case synonym : 'except'
### FINALLY
> `JooseX.CPS.Continuation FINALLY(Function func, Object scope?)`
> Add a `FINALLY` statement to the current continuation.
> Has a lower-case synonym : 'ensure'
### NEXT
> `JooseX.CPS.Continuation NEXT(Function func, Object scope?, Array args?)`
> Derive a sibling continuation, chaining it after itself. Return newly created continuation.
> Has a lower-case synonym : 'next'
### AND
> `JooseX.CPS.Continuation AND(Function func, Object scope?, Array args?)`
> Alias for `TRY` that also switch a type of the continuation to `Parallel`.
> Has a lower-case synonym : 'and'
### NOW
> `JooseX.CPS.Continuation NOW()`
> Activates current continuation graph by looking the 1st not yet entered continuation. After finding it, delegate to its `entry` method with the passed arguments.
> Has a lower-case synonym : 'now'
### getCONTINUE
> `Function getCONTINUE()`
> Return a function, binded to the `CONTINUE` method of itself.
### getRETURN
> `Function getRETURN()`
> Return a function, binded to the `RETURN` method of itself.
### getTHROW
> `Function getTHROW()`
> Return a function, binded to the `THROW` method of itself.
GETTING HELP
============
This extension is supported via github issues tracker: <http://github.com/SamuraiJack/JooseX-CPS/issues>
For general Joose questions you can also visit the [#joose](http://webchat.freenode.net/?randomnick=1&channels=joose&prompt=1) on irc.freenode.org, or the forum at <http://joose.it/forum>
SEE ALSO
========
[Main documentation page](../CPS.html)
General documentation for Joose: <http://openjsan.org/go/?l=Joose>
AUTHORS
=======
Nickolay Platonov [nplatonov@cpan.org](mailto: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.
*/
;
Class('JooseX.CPS.MethodModifier.Override', {
meta : Joose.Meta.Class,
isa : Joose.Managed.Property.MethodModifier.Override,
use : 'JooseX.CPS.Continuation',
methods : {
prepareWrapper : function (params) {
var overriden = this.SUPER(params)
var continued = function () {
var cont = Joose.top.__GLOBAL_CNT__ || new JooseX.CPS.Continuation()
return cont.TRY(overriden, this, arguments)
}
continued.IS_CONTINUED = true
return continued
}
}
})
;
Class('JooseX.CPS.MethodModifier.After', {
meta : Joose.Meta.Class,
isa : Joose.Managed.Property.MethodModifier,
use : 'JooseX.CPS.Continuation',
methods : {
prepareWrapper : function (params) {
var name = params.name
var modifier = params.modifier
var isOwn = params.isOwn
var original = params.target.prototype[name]
var superProto = params.superProto
var originalCall = params.originalCall
var continuedOriginal = function () {
var isContinued = isOwn ? original.IS_CONTINUED : superProto[name].IS_CONTINUED
if (isContinued)
originalCall.apply(this, arguments).NOW()
else
this.CONTINUE(originalCall.apply(this, arguments))
}
var continued = function () {
var cont = Joose.top.__GLOBAL_CNT__ || new JooseX.CPS.Continuation()
var args = arguments
return cont.TRY(continuedOriginal, this, arguments).THEN(function () {
var res = arguments
this.CONT.TRY(modifier, this, args).THEN(function () {
this.CONTINUE.apply(this, res)
}).NOW()
}, this)
}
continued.IS_CONTINUED = true
return continued
}
}
});
Class('JooseX.CPS.MethodModifier.Before', {
meta : Joose.Meta.Class,
isa : Joose.Managed.Property.MethodModifier,
use : 'JooseX.CPS.Continuation',
methods : {
prepareWrapper : function (params) {
var name = params.name
var modifier = params.modifier
var isOwn = params.isOwn
var original = params.target.prototype[name]
var superProto = params.superProto
var originalCall = params.originalCall
var then = function () {
var isContinued = isOwn ? original.IS_CONTINUED : superProto[name].IS_CONTINUED
if (isContinued)
originalCall.apply(this, arguments).NOW()
else
this.CONTINUE(originalCall.apply(this, arguments))
}
var continued = function () {
var cont = Joose.top.__GLOBAL_CNT__ || new JooseX.CPS.Continuation()
return cont.TRY(function () {
this.CONT.TRY(modifier, this, arguments).NOW()
}, this, arguments).THEN(then, this, arguments)
}
continued.IS_CONTINUED = true
return continued
}
}
});
Class('JooseX.CPS.MethodModifier.Put', {
isa : 'JooseX.CPS.MethodModifier.Override',
methods : {
prepareWrapper : function (params) {
if (params.isOwn) throw "Method [" + params.name + "] is applying over something [" + params.originalCall + "] in class [" + params.target + "]"
return this.SUPER(params)
}
}
});
Class('JooseX.CPS.Builder', {
meta : Joose.Meta.Class,
use : [
'JooseX.CPS.MethodModifier.Put',
'JooseX.CPS.MethodModifier.After',
'JooseX.CPS.MethodModifier.Before',
'JooseX.CPS.MethodModifier.Override'
],
isa : Joose.Managed.Builder,
methods : {
methods : function (meta, info) {
var methods = meta.stem.properties.methods
Joose.O.eachOwn(info, function (value, name) {
methods.addProperty(name, {
meta : JooseX.CPS.MethodModifier.Put,
init : value
})
})
},
after : function (meta, info) {
Joose.O.each(info, function (value, name) {
meta.addMethodModifier(name, value, JooseX.CPS.MethodModifier.After)
})
},
before : function (meta, info) {
Joose.O.each(info, function (value, name) {
meta.addMethodModifier(name, value, JooseX.CPS.MethodModifier.Before)
})
},
override : function (meta, info) {
Joose.O.each(info, function (value, name) {
meta.addMethodModifier(name, value, JooseX.CPS.MethodModifier.Override)
})
},
have : function () {
throw "'have' builder is not supported in the 'continued' section"
},
havenot : function () {
throw "'havenot' builder is not supported in the 'continued' section"
},
around : function () {
throw "'around' builder is not supported in the 'continued' section"
},
augment : function () {
throw "'augment' builder is not supported in the 'continued' section"
},
does : function () {
throw "'does' builder is not supported in the 'continued' section"
},
doesnot : function () {
throw "'doesnot' builder is not supported in the 'continued' section"
}
}
})
;
Role('JooseX.CPS.ControlFlow', {
use : [ 'JooseX.CPS.Continuation' ],
has : {
CONT : null
},
methods : {
TRY : function (func, scope, args) {
return this.CONT.TRY(func, scope || this, args)
},
AND : function (func, scope, args) {
return this.CONT.AND(func, scope || this, args)
},
THEN : function (func, scope, args) {
return this.CONT.THEN(func, scope || this, args)
},
andTHEN : function (func, scope, args) {
return this.CONT.andTHEN(func, scope || this, args)
},
NEXT : function (func, scope, args) {
return this.CONT.NEXT(func, scope || this, args)
},
NOW : function () {
var cont = this.CONT
return cont.NOW.apply(cont, arguments)
},
CONTINUE : function () {
var cont = this.CONT
cont.CONTINUE.apply(cont, arguments)
},
RETURN : function () {
var cont = this.CONT
cont.RETURN.apply(cont, arguments)
},
THROW : function () {
var cont = this.CONT
cont.THROW.apply(cont, arguments)
},
getCONTINUE : function () {
return this.CONT.getCONTINUE()
},
getRETURN : function () {
return this.CONT.getRETURN()
},
getTHROW : function () {
return this.CONT.getTHROW()
}
}
//eof methods
});
Role('JooseX.CPS', {
/*VERSION*/VERSION : 0.12,
use : [ 'JooseX.CPS.Builder', 'JooseX.CPS.ControlFlow' ],
has : {
continuedBuilder : null
},
after : {
processStem : function () {
this.continuedBuilder = new JooseX.CPS.Builder({ targetMeta : this })
this.addRole(JooseX.CPS.ControlFlow)
}
},
builder : {
methods : {
continued : function (meta, info) {
meta.continuedBuilder._extend(info)
}
}
}
});