

var joosetop = this;

Joose = function () {
    this.cc              = null;  
    this.currentModule   = null
    this.top             = joosetop;
    this.globalObjects   = [];
    
    this.anonymouseClassCounter = 0;
};

Joose.A = {};
Joose.A.each = function (array, func) {
    for(var i = 0; i < array.length; i++) {
        func(array[i], i)
    }
}
Joose.A.exists = function (array, value) {
    for(var i = 0; i < array.length; i++) {
        if(array[i] == value) {
            return true
        }
    }
    return false
}
Joose.A.concat = function (source, array) {
    source.push.apply(source, array)
    return source
}

Joose.A.grep = function (array, func) {
    var a = [];
    Joose.A.each(array, function (t) {
        if(func(t)) {
            a.push(t)
        }
    })
    return a
}
Joose.A.remove = function (array, removeEle) {
    var a = [];
    Joose.A.each(array, function (t) {
        if(t !== removeEle) {
            a.push(t)
        }
    })
    return a
}

Joose.S = {};
Joose.S.uppercaseFirst = function (string) { 
    var first = string.substr(0,1);
    var rest  = string.substr(1,string.length-1);
    first = first.toUpperCase()
    return first + rest;
}

Joose.S.isString = function (thing) { 
    if(typeof thing == "string") {
        return true
    }
    return false
}

Joose.O = {};
Joose.O.each = function (object, func) {
    for(var i in object) {
        func(object[i], i)
    }
}

Joose.O.eachSafe = function (object, func) {
    for(var i in object) {
        if(object.hasOwnProperty(i)) {
            func(object[i], i)
        }
    }
}

Joose.O.extend = function (target, newObject) {
    for(var i in newObject) {
        var thing = newObject[i]
        target[i] = thing
    }
}


Joose.prototype = {
    
    addToString: function (object, func) {
        object.toString = func;
    },
    
    isInstance: function(obj) {
        if(!obj.meta) {
            throw "isInstance only works with Joose objects and classes."
        }
        if(obj.constructor === obj.meta.c) {
            return true
        }
        return false
    },
    
    init: function () {
        this.builder = new Joose.Builder();
        this.builder.globalize()
    },
components: function () {
        return [
            "Joose.Builder",
            "Joose.Class",
            "Joose.Method",
            "Joose.ClassMethod",
            "Joose.Attribute",
            "Joose.Role",
            "Joose.Singleton",
            "Joose.SimpleRequest",
            "Joose.Gears",
            "Joose.Storage",
            "Joose.Storage.Unpacker",
            "Joose.Decorator",
            "Joose.Module",
            "Joose.TypeChecker",
            "Joose.TypeConstraint",
            "Joose.TypeCoercion",
            "Joose.Types",
            "Joose.Prototype",
            "Joose.TypedMethod",
            "Joose.MultiMethod"
        ]
    },

    loadComponents: function (basePath) {
        var html = "";
        Joose.A.each(this.components(), function (name) {
            var url    = ""+basePath + "/" + name.split(".").join("/") + ".js";
           
            html += '<script type="text/javascript" src="'+url+'"></script>'
        })
        document.write(html)
    }
}

Joose.copyObject = function (source, target) {
    var keys = "";
    Joose.O.each(source, function (value, name) {  keys+=", "+name; target[name] = value })
    return target
};



Joose.emptyFunction = function () {};

this.joose = new Joose();

(function () {
    
    if(
         typeof this["load"] == "function" &&
         (
            typeof this["Packages"] == "function" ||
            typeof this["Packages"] == "object"
         )
   ) {
        joose.CHAOTIC_TRAVERSION_ORDER = true
   }
})()


Joose.bootstrap = function () {
    var BOOT = new Joose.MetaClassBootstrap(); 
    
    BOOT.builder    = Joose.MetaClassBootstrap;

    Joose.MetaClass = BOOT.createClass("Joose.MetaClass");
   
    Joose.MetaClass.meta.addNonJooseSuperClass("Joose.MetaClassBootstrap", BOOT)
    
    Joose.MetaClass.meta.addMethod("initialize", function () { this._name = "Joose.MetaClass" })

    var META     = new Joose.MetaClass();
    
    META.builder = Joose.MetaClass;
    
    Joose.Class  = META.createClass("Joose.Class")
    Joose.Class.meta.addSuperClass(Joose.MetaClass);
    Joose.MetaClass.meta.addMethod("initialize", function () { this._name = "Joose.Class" })
    
    Joose.Class.create = function (name, optionalConstructor, optionalModule) {
        var aClass      = new this();
        
        aClass.builder  = this;
        var c           = aClass.createClass(name, optionalConstructor, optionalModule)
        c.meta.builder  = this
        
        return c;
    }
}

Joose.bootstrapCompletedBuilder = function () {
    Joose.Builder.Globals.joosify("Joose.Method", Joose.Method)
    Joose.Builder.Globals.joosify("Joose.Attribute", Joose.Attribute)
    
}

Joose.bootstrapCompletedClassMethod = function () {
    Joose.Class.meta.addClassMethod("create", Joose.Class.create)
}

Joose.bootstrap3 = function () {
    }

Joose.MetaClassBootstrap = function () {
    this._name            = "Joose.MetaClassBootstrap";
    this.methodNames      = [];
    this.attributeNames   = ["_name", "isAbstract", "isDetached", "methodNames", "attributeNames", "methods", "parentClasses", "roles", "c"];
    this.attributes       = {};
    this.methods          = {};
    this.classMethods     = {};
    this.parentClasses    = [];
    this.roles            = []; 
    this.myRoles          = []; 
    this.isAbstract       = false;
    this.isDetached       = false;
}
Joose.MetaClassBootstrap.prototype = {
    
    toString: function () {
        if(this.meta) {
            return "a "+this.meta.className();
        }
        return "NoMeta"
    },
    
    className: function () {
        return this._name
    },
    
    getName: function () {
        return this.className()
    },
    
    newMetaClass: function () {
        
        var me  = this;
        
        var metaClassClass = this.builder;
        
        var c     = new metaClassClass();
        c.builder = metaClassClass;
        c._name   = this._name
        
        c.methodNames    = [];
        c.attributeNames = [];
        c.methods        = {};
        c.classMethods   = {};
        c.parentClasses  = [];
        c.roles          = [];
        c.myRoles        = [];
        c.attributes     = {};
        
        var myMeta = this.meta;
        if(!myMeta) {
            myMeta = this;
        }
        
        c.meta = myMeta
        
        return c
    },
    
    createClass:    function (name, optionalConstructor, optionalModuleObject) {
        var meta  = this.newMetaClass();
        
        var c;
        
        if(optionalConstructor) {
            c = optionalConstructor
        } else {
            c = this.defaultClassFunctionBody()
            
            if(optionalModuleObject) {
                optionalModuleObject.addElement(c)
                }
        }
        
        c.prototype.meta = meta
        c.meta    = meta;
        if(name == null) {
            meta._name = "__anonymous__" 
        } else {
            var className = name;
            if(optionalModuleObject) {
                className = optionalModuleObject.getName() + "." + name
            }
            meta._name = className;
        }
        meta.c = c;
        
        if(!optionalModuleObject) {
            joose.globalObjects.push(c)
        }
        
        meta.addInitializer();
        meta.addToString();
        meta.addDetacher();
        
        return c;
    },
    
    buildComplete: function () {
        },
    
    wI: function (props) {
        this._initializeFromProps(props)
    },
    
    _initializeFromProps: function (props) {
        var me = this;
        if(props) {
            
            if(joose.CHAOTIC_TRAVERSION_ORDER) {
                Joose.A.each(["isa", "does", "has", "method", "methods"], function (name) {
                    if(name in props) {
                        var value = props[name];
                        me._initializeFromProp(name, value, props)
                        delete props[name]
                    }
                })
            }
            
            Joose.O.eachSafe(props, function (value, name) {
                me._initializeFromProp(name, value, props)
            })
            
            for(var i = 0; i < this.roles.length; i++) {
                var role = this.roles[i];
                role.meta.applyMethodModifiers(this.c)
            }
            
            me.buildComplete();     
            me.validateClass();
        }
    },
    
    _initializeFromProp: function (propName, value, props) {
        var paras             = value;
        var customBuilderName = "handleProp"+propName;
        if(this.meta.can(customBuilderName)) {
            this[customBuilderName](paras, props)
        } else { 
            throw new Error("Called invalid builder "+propName+" while creating class "+this.className())
        }
    },
    
    instantiate: function () {
        var f = function () {};
        f.prototype = this.c.prototype;
        f.prototype.constructor = this.c;
        var obj = new f();
        this.c.apply(obj, arguments);
        return obj;
    },
    
    defaultClassFunctionBody: function () {
        var f = function () {
            this.initialize.apply(this, arguments);
        };
        joose.addToString(f, function () {
            return this.meta.className()
        })
        return f;
    },
    
    addToString: function () {
        this.addMethod("toString", function () {
            if(this.stringify) {
                return this.stringify()
            }
            return "a "+ this.meta.className()
        })
    },
    
    addInitializer: function () {
        if(!this.c.prototype.initialize) {
            this.addMethod("initialize", this.initializer())
        }
    },
    
    initializer: function () {
        return function initialize (paras) {
            var me = this;
            if(this.meta.isAbstract) {
                var name = this.meta.className();
                throw ""+name+" is an abstract class and may not instantiated."
            }
            var attributes = this.meta.getAttributes();
            for(var i in attributes) {
                if(attributes.hasOwnProperty(i)) {
                    var attr = attributes[i];
                    attr.doInitialization(me, paras);
                }
            }
        }
    },
    
    dieIfString: function (thing) {
        if(Joose.S.isString(thing)) {
            throw new TypeError("Parameter must not be a string.")
        }
    },
    
    addRole: function (roleClass) {
        this.dieIfString(roleClass);
        var c = this.getClassObject();
        if(roleClass.meta.apply(c)) {
            this.roles.push(roleClass);
            this.myRoles.push(roleClass);
        }
        
    },
    
    getClassObject: function () {
        return this.c
    },
    
    classNameToClassObject: function (className) {
        var top    = joose.top;
        var parts  = className.split(".");
        var object = top;
        for(var i = 0; i < parts.length; i++) {
            var part = parts[i];
            object   = object[part];
            if(!object) {
                throw "Unable to find class "+className
            }
        }
        return object
    },
    
    addNonJooseSuperClass: function (name, object) {
        
        var pseudoMeta     = new Joose.MetaClassBootstrap();
        pseudoMeta.builder = Joose.MetaClassBootstrap;
        var pseudoClass    = pseudoMeta.createClass(name)
        
        Joose.O.each(object, function(value, name) {
            if(typeof(value) == "function") {
                pseudoClass.meta.addMethod(name, value)
            } else {
                pseudoClass.meta.addAttribute(name, {init: value})
            }
        })
        
        this.addSuperClass(pseudoClass);
    },
    
    addSuperClass:    function (classObject) {
        this.dieIfString(classObject);
        var me    = this;
        
        var names = classObject.meta.getMethodNames();
        for(var i = 0; i < names.length; i++) {
            var name = names[i]
            
            var m = classObject.meta.getMethodObject(name)
            if(m) {
                var method = m.copy();
                method.setIsFromSuperClass(true);
                me.addMethodObject(method)
            }
            m = classObject.meta.getClassMethodObject(name)
            if(m) {
                var method = m.copy();
                method.setIsFromSuperClass(true);
                me.addMethodObject(method)
            }
        } 
        
        Joose.O.eachSafe(classObject.meta.attributes, function (attr, name) {
            me.addAttribute(name, attr.getProps())
        })
        
        var roles = classObject.meta.roles
        for(var i = 0; i < roles.length; i++) {
            var role = roles[i]
            me.roles.push(role)
        }
        
        this.parentClasses.unshift(classObject)
    },
    
    _fixMetaclassIncompatability: function (superClass) {
        
        var superMeta     = superClass.meta;
        var superMetaName = superMeta.meta.className();
        
        if(
          superMetaName == "Joose.Class"     ||
          superMetaName == "Joose.MetaClass" || 
          superMetaName == "Joose.MetaClassBootstrap") {
            return
        }
        
        if(this.meta.meta.isa(superMeta)) {
            return
        }
        
        var patched = superMeta.meta.instantiate(this);
        
        for(var i in patched) {
            this[i] = patched[i]
        }
    },
    
    isa:            function (classObject) {
        this.dieIfString(classObject);
        var name = classObject.meta.className()
        if(this.className() == name) {
            return true
        }
        for(var i = 0; i < this.parentClasses.length; i++) {
            var parent = this.parentClasses[i].meta
            if(parent.className() == name) {
                return true
            }
            if(parent.isa(classObject)) {
                return true
            }
        }
        return false
    },
    
    wrapMethod:  function (name, wrappingStyle, func, notPresentCB) {
        
        var orig = this.getMethodObject(name);
        if(orig) {
            this.addMethodObject( orig[wrappingStyle](func) )
        } else {
            if(notPresentCB) {
                notPresentCB()
            } else {
                throw new Error("Unable to apply "+wrappingStyle+" method modifier because method "+name+" does not exist");
            }
        }
    },
    
    dispatch:        function (name) {
        return this.getMethodObject(name).asFunction()
    },
    
    hasMethod:         function (name) {
        return this.methods[name] != null || this.classMethods[name] != null
    },
    
    addMethod:         function (name, func, props) {
        var m = new Joose.Method(name, func, props);
        
        this.addMethodObject(m)
    },
    
    addClassMethod:         function (name, func, props) {
        var m = new Joose.ClassMethod(name, func, props);
        
        this.addMethodObject(m)
    },
    
    addMethodObject:         function (method) {
        var m              = method;
        var name           = m.getName === Joose.Method.prototype.getNname ? m._name : m.getName();
        
        var body = m._body;
        if(!body.displayName) { 
            var className = this.className === Joose.MetaClassBootstrap.prototype.className ? this._name : this.className()
            body.displayName =  className + "." + name+"()";
        }
        
        if(!this.methods[name] && !this.classMethods[name]) {
            this.methodNames.push(name);
        }
        if(m._isClassMethod) {
            this.classMethods[name] = m;
        } else {
            this.methods[name] = m;
        }
        
        method.addToClass(this.c)
    },
    
    attributeMetaclass: function () {
        return Joose.Attribute
    },
    
    addAttribute:     function (name, props) {
        
        var metaclass = this.attributeMetaclass();
        
        if(props && props.metaclass) {
            metaclass = props.metaclass
        }
        
        var at = new metaclass(name, props);
        
        at.apply(this.c)
    },
    
    getAttributes: function () {
        return this.attributes
    },
    
    getAttribute: function (name) {
        return this.attributes[name]
    },
    
    setAttribute: function (name, attributeObject) {
        return this.attributes[name] = attributeObject
    },
    
    getMethodObject: function (name) {
        return this.methods[name]
    },
    
    getClassMethodObject: function (name) {
        return this.classMethods[name]
    },
    
    getAttributeNames: function () {
        return this.attributeNames;
    },
    
    getInstanceMethods: function () {
        var a = [];
        Joose.O.eachSafe(this.methods, function (m) {
            a.push(m)
        })
        return a
    },
    
    getClassMethods: function () {
        var a = [];
        Joose.O.eachSafe(this.classMethods, function (m) {
            a.push(m)
        })
        return a
    },

    getSuperClasses:    function () {
        return this.parentClasses;
    },
    
    getSuperClass:    function () {
        return this.parentClasses[0];
    },
    
    getRoles:    function () {
        return this.roles;
    },
    
    getMethodNames:    function () {
        return this.methodNames;
    },
    
    makeAnonSubclass: function () {
        var c    = this.createClass(this.className()+"__anon__"+joose.anonymouseClassCounter++);
        c.meta.addSuperClass(this.getClassObject());
        
        return c;
    },
    
    addDetacher: function () {
        this.addMethod("detach", function detach () {
            var meta = this.meta;
            
            if(meta.isDetached) {
                return 
            } 
            
            var c    = meta.makeAnonSubclass()
            
            c.meta.isDetached = true;
            
            this.meta      = c.meta;
            this.constructor = c;
            
            var proto;
            
            if(!this.__proto__) {
                proto = this
            } else {
                proto   = {};
                Joose.copyObject(this, proto)
            }
            
            
            c.prototype    = proto;
            this.__proto__ = c.prototype
            return
        })
    },
    
    validateClass: function () {
        var c  = this.getClassObject();
        var me = this;
        
        var throwException = true;
        Joose.A.each(this.roles, function(role) {
              role.meta.isImplementedBy(c, throwException)
        })
    },
    
            can: function (methodName) {
        var method = this.methods[methodName];
        if(!method) {
            return false
        }
        return true
    },
    
    classCan: function (methodName) {
        var method = this.classMethods[methodName];
        if(!method) {
            return false
        }
        return true
    },
    
    
    does: function (roleObject) {
        
        for(var i = 0; i < this.roles.length; i++) {
            if(roleObject === this.roles[i]) {
                return true
            }
        }
        
        for(var i = 0; i < this.roles.length; i++) {
            if(this.roles[i].meta.does(roleObject)) {
                return true
            }
        }
        
        return false
        },
    
    implementsMyMethods: function (classObject) {
        var complete = true
        Joose.A.each(this.getMethodNames(), function (value) {
            var found = classObject.meta.can(value)
            if(!found) {
                complete = false
            }
        })
        return complete
    },
    
    handleProprequires:    function (methodName) {
        var me = this;
        if(!this.meta.isa(Joose.Role)) {
            throw("Keyword 'requires' only available classes with a meta class of type Joose.Role")
        }
        if(methodName instanceof Array) {
            Joose.A.each(methodName, function (name) {
                me.addRequirement(name)
            })
        } else {
            me.addRequirement(methodName)
        }
    },
    
    handlePropisAbstract: function (bool) {
        this.isAbstract = bool
    },
    
    
    handlePropisa:    function (classObject) {
        if(classObject == null) {
            throw new Error("Super class is null")
        }
        this.addSuperClass(classObject)
    },
    handlePropdoes:    function (role) {
        var me = this;
        if(role instanceof Array) {
            Joose.A.each(role, function (aRole) {
                me.addRole(aRole)
            })
        } else {
            me.addRole(role)
        }
        
    },
    
    handleProphas:    function (map) {
        var me = this;
        if(typeof map == "string") {
            var name  = arguments[0];
            var props = arguments[1];
            me.addAttribute(name, props)
        } else { 
            Joose.O.eachSafe(map, function (props, name) {
                me.addAttribute(name, props)
            })
        }
    },
    
    handlePropmethod: function (name, func, props) {
        this.addMethod(name, func, props)
    },
    
    handlePropmethods: function (map) {
        var me = this
        Joose.O.eachSafe(map, function (func, name) {
            if(typeof func !== "function") {
                var props  = func; 
                var method;
                if (props instanceof Array) {
                    var patterns = props; 
                                          method = new Joose.MultiMethod
                        .newFromPatterns(name, patterns);
                } else {
                    method = Joose.TypedMethod.newFromProps(name, props)
                }
                me.addMethodObject(method)
            } 
            else {
                me.addMethod(name, func)
            }
        })
    },
    
    handlePropclassMethods: function (map) {
        var me = this;
        Joose.O.eachSafe(map, function (func, name2) {
            me.addMethodObject(new Joose.ClassMethod(name2, func))
        })
    },
    
    handlePropworkers: function (map) {
        var me = this;
        Joose.O.eachSafe(map, function (func, name) {
            me.addWorker(name, func)
        })
    },
    
    handlePropbefore: function(map) {
        var me = this
        Joose.O.eachSafe(map, function (func, name) {
            me.wrapMethod(name, "before", func);
        }) 
    },
    
    handlePropafter: function(map) {
        var me = this
        Joose.O.eachSafe(map, function (func, name) {
            me.wrapMethod(name, "after", func);
        }) 
    },
    
    handleProparound: function(map) {
        var me = this
        Joose.O.eachSafe(map, function (func, name) {
            me.wrapMethod(name, "around", func);
        }) 
    },
    
    handlePropoverride: function(map) {
        var me = this
        Joose.O.eachSafe(map, function (func, name) {
            me.wrapMethod(name, "override", func);
        }) 
    },
    
    handlePropaugment: function(map) {
        var me = this
        Joose.O.eachSafe(map, function (func, name) {
            me.wrapMethod(name, "augment", func, function () {
                me.addMethod(name, func)
            });
        }) 
    },
    
    handlePropdecorates: function(map) {
        var me = this
        Joose.O.eachSafe(map, function (classObject, attributeName) {
            me.decorate(classObject, attributeName)
        }) 
    }
};

Joose.Attribute = function (name, props) {
    this.initialize(name, props)
}

Joose.Attribute.prototype = {
    
    _name:  null,
    _props: null,
    
    getName:    function () { return this._name },
    getProps:    function () { return this._props },
    
    initialize: function (name, props) {
        this._name  = name;
        this.setProps(props);
    },
    
    setProps: function (props) {
        if(props) {
            this._props = props
        } else {
            this._props = {};
        }
    },
    
    getIsa: function () {
        var props = this.getProps();
        if("isa" in props && props.isa == null) {
            throw new Error("You declared an isa property but the property is null.")
        }
        if(props.isa) {
            if(!props.isa.meta) {
                return props.isa()
            }
            return props.isa
        }
        return
    },
    
    addSetter: function (classObject) {
        var meta  = classObject.meta;
        var name  = this.getName();
        var props = this.getProps();
        
        var setterName = this.setterName();
        
        if(meta.can(setterName)) { 
            return
        }
        
        var isa   = this.getIsa();

        var func;
        if(isa) {
            
            var checkerFunc = Joose.TypeChecker.makeTypeChecker(isa, props, "attribute", name)
        	
        	func = function setterWithIsaCheck (value, errorHandler) {
                value = checkerFunc(value, errorHandler)
                this[name] = value
                return this;
            }
        } else {
            func = function setter (value) {
                this[name] = value
                return this;
            }
        }
        meta.addMethod(setterName, func);
    },
    
    
    addGetter: function (classObject) {
        var meta  = classObject.meta;
        var name  = this.getName();
        var props = this.getProps()
        
        var getterName = this.getterName();
        
        if(meta.can(getterName)) { 
            return 
        }
        
        var func  = function getter () {
            return this[name]
        }
        
        var init  = props.init;
        
        if(props.lazy) {
            func = function lazyGetter () {
                var val = this[name];
                if(typeof val == "function" && val === init) {
                    this[name] = val.apply(this)
                }
                return this[name]
            }
        }
        
        meta.addMethod(getterName, func);
    },
    
    initializerName: function () {
        return this.toPublicName()
    },
    
    getterName: function () {
        if(this.__getterNameCache) { 
            return this.__getterNameCache
        }
        this.__getterNameCache = "get"+Joose.S.uppercaseFirst(this.toPublicName())
        return this.__getterNameCache;
    },
    
    setterName: function () {
        if(this.__setterNameCache) { 
            return this.__setterNameCache
        }
        this.__setterNameCache = "set"+Joose.S.uppercaseFirst(this.toPublicName())
        return this.__setterNameCache;
    },
    
    isPrivate: function () {
        return this.getName().charAt(0) == "_"
    },
    
    toPublicName: function () {
        
        if(this.__publicNameCache) { 
            return this.__publicNameCache
        }
        
        var name = this.getName();
        if(this.isPrivate()) {
            this.__publicNameCache = name.substr(1)
            return this.__publicNameCache;
        }
        this.__publicNameCache = name
        return this.__publicNameCache
    },
    
    handleIs: function (classObject) {
        var meta  = classObject.meta;
        var name  = this.getName();
        var props = this.getProps();
        
        var is    = props.is;

        if(is == "rw" || is == "ro") {
            this.addGetter(classObject);
        }
        if(is == "rw") {
            this.addSetter(classObject)
        }
    },
    
    handleInit: function (classObject) {
        var props = this.getProps();
        var name  = this.getName();
        
        classObject.prototype[name]     = null;
        if(typeof props.init != "undefined") {
            var val = props.init;
            var type = typeof val;

            classObject.prototype[name] = val;
        }
    },
    
    handleProps: function (classObject) {
        this.handleIs(classObject);
        this.handleInit(classObject)
    },
    
    apply: function (classObject) {
        
        var meta  = classObject.meta;
        var name  = this.getName();
        
        this.handleProps(classObject)
        
        meta.attributeNames.push(name)
        
        meta.setAttribute(name, this)
        meta.attributes[name] = this;
    }
    
    
}

Joose.Method = function (name, func, props) {
    this.initialize(name, func, props)
}

Joose.Method.prototype = {
    
    _name: null,
    _body: null,
    _props: null,
    _isFromSuperClass: false,
    _isClassMethod: false,
    
    getName:    function () { return this._name },
    getBody:    function () { return this._body },
    getProps:   function () { return this._props },
    
    isFromSuperClass: function () {
        return this._isFromSuperClass
    },
    
    setIsFromSuperClass: function (bool) {
        this._isFromSuperClass = bool
    },
    
    copy: function () {
        return new Joose.Method(this.getName(), this.getBody(), this.getProps())
    },
    
    initialize: function (name, func, props) {
        this._name  = name;
        this._body  = func;
        this._props = props;
        
        func.name   = name
    
        func.meta   = this
    },
    
    isClassMethod: function () { return this._isClassMethod },
    
    apply:    function (thisObject, args) {
        return this._body.apply(thisObject, args)
    },
    
    addToClass: function (c) {
        var base = Joose.Method.prototype;
        var name = this.getName === base.getName ? this._name : this.getName();
        var func = this.asFunction === base.asFunction ? this._body : this.asFunction()
        c.prototype[name] = func
    },
    
    
    asFunction:    function () {
        return this._body
    }
}



Joose.bootstrap()


Joose.Builder = function () {
    this.globalize = function () {
        Joose.O.each(Joose.Builder.Globals, function (func, name) {
            var globalName = "Joose"+name
            if(typeof joose.top[name] == "undefined") {
                joose.top[name] = func
            }
            
            joose.top[globalName] = func
        });
    }
}

Joose.Builder.Globals = {
    Module: function (name, functionThatCreatesClassesAndRoles) {
        return Joose.Module.setup(name, functionThatCreatesClassesAndRoles)
    },
    
    Role: function (name, props) {
        if(!props.meta) {
            props.meta = Joose.Role;
        }
        return JooseClass(name, props)
    },
    
    Prototype: function (name, props) {
        if(!props.meta) {
            props.meta = Joose.Prototype;
        }
        return JooseClass(name, props);
    },
    
    Class:    function (name, props) {
var c = null;
        
        if(name) {
            var className  = name;
            if(joose.currentModule) {
                className  = joose.currentModule.getName() + "." + name
            }
            var root       = joose.top;
            var parts      = className.split(".")
        
            for(var i = 0; i < parts.length; i++) {
                root = root[parts[i]]
            }
            c = root;
        }

        if(c == null) {
            
            var metaClass;
            
            if(props && props.meta) {
                metaClass = props.meta
                delete props.meta
            }
            else if(props && props.isa && props.isa != Joose.Class) {
                metaClass = props.isa.meta.builder
                }
            else {
                metaClass   = Joose.Class;
            }
            
            var c = metaClass.create(name, null, joose.currentModule)
            
            var className   = c.meta.className()
            
            if(name && className) {
                var root = joose.top;
                var n = new String(className);
                var parts = n.split(".");
                for(var i = 0; i < parts.length - 1; i++) {
                    if(root[parts[i]] == null) {
                        root[parts[i]] = {};
                    }
                    root = root[parts[i]];
                }
                root[parts[parts.length - 1]] = c
            }
            
        }
        
        c.meta.wI(props)
        
        return c
    },
    
    Type: function (name, props) {
        var isAnon = false
        if(arguments.length == 1 && name instanceof Object) {
            props  = name;
            isAnon = true;
        }
        
        if(props instanceof RegExp || props instanceof Function) {
            props = {
                where: props
            }
        }
        
        if(isAnon) {
            name   = "AnonType: "+(props.where ? props.where.toString() : "");
        }
        
        var t = Joose.TypeConstraint.newFromTypeBuilder(name, props);
        
        if(!isAnon) {
            var m = joose.currentModule
        
            if(!m) {
                JooseModule("Joose.Type");
                if(typeof joose.top.TYPE == "undefined") {
                    joose.top.TYPE = Joose.Type;
                }
                m = Joose.Type.meta;
            }
        
            m.addElement(t)
            m.getContainer()[name] = t;
        }
        return t
    },
    
    joosify: function (standardClassName, standardClassObject) {
        var c         = standardClassObject;
        var metaClass = new Joose.Class();
        metaClass.builder = Joose.Class;
        
        c.toString = function () { return this.meta.className() }
        c             = metaClass.createClass(standardClassName, c)
    
        var meta = c.meta;
    
        for(var name in standardClassObject.prototype) {
            if(name == "meta") {
                continue
            }
            var value = standardClassObject.prototype[name]
            if(typeof(value) == "function") {
                meta.addMethod(name, value)
            } else {
                var props = {};
                if(typeof(value) != "undefined") {
                    props.init = value
                }
                meta.addAttribute(name, props)
            }
        }
        
        return c
    },
    
    rw: "rw",
    ro: "ro"
};

joose.init();
Joose.bootstrapCompletedBuilder();


(function (Class) {

Class("Joose.Method", {
    methods: {
        
        copy: function () {
            return this.meta.instantiate(this.getName(), this.getBody(), this.getProps())
        },
        
        _makeWrapped: function (func) {
            return this.meta.instantiate(this.getName(), func); 
        },
        
        around: function (func) {
            var orig = this.getBody();
            return this._makeWrapped(function aroundWrapper () {
                var me = this;
                var bound = function () { return orig.apply(me, arguments) }
                return func.apply(this, Joose.A.concat([bound], arguments))
            })            
        },
        before: function (func) {
            var orig = this.getBody();
            return this._makeWrapped(function beforeWrapper () {
                func.apply(this, arguments)
                return orig.apply(this, arguments);
            })        
        },
        after: function (func) {
            var orig = this.getBody();
            return this._makeWrapped(function afterWrapper () {
                var ret = orig.apply(this, arguments);
                func.apply(this, arguments);
                return ret
            })
        },
        
        override: function (func) {
            var orig = this.getBody();
            return this._makeWrapped(function overrideWrapper () {
                var me      = this;
                var bound   = function () { return orig.apply(me, arguments) }
                var before  = this.SUPER;
                this.SUPER  = bound;
                var ret     = func.apply(this, arguments);
                this.SUPER  = before;
                return ret
            })            
        },
        
        augment: function (func) {
            var orig = this.getBody();
            orig.source = orig.toString();
            return this._makeWrapped(function augmentWrapper () {
                var exe       = orig;
                var me        = this;
                var inner     = func
                inner.source  = inner.toString();
                if(!this.__INNER_STACK__) {
                    this.__INNER_STACK__ = [];
                };
                this.__INNER_STACK__.push(inner)
                var before    = this.INNER;
                this.INNER    = function () {return  me.__INNER_STACK__.pop().apply(me, arguments) };
                var ret       = orig.apply(this, arguments);
                this.INNER    = before;
                return ret
            })
        }
    }
})

})(JooseClass);

(function (Class) {
    
Class("Joose.ClassMethod", {
    isa: Joose.Method,
    after: {
        initialize: function () {
            this._isClassMethod = true
        }
    },
    methods: {
        addToClass: function (c) {
            c[this.getName()] = this.asFunction()
        },
        
        copy: function () {
            return new Joose.ClassMethod(this.getName(), this.getBody(), this.getProps())
        }
    }
})

Joose.bootstrapCompletedClassMethod()

})(JooseClass);

(function (Class) {
Class("Joose.Attribute", {
    after: {
        handleProps: function (classObject) {
            this.handleHandles(classObject);
            this.handlePredicate(classObject);
        }
    },
    methods: {
        
        isPersistent: function () {
            var props = this.getProps()
            if(props.persistent == false) {
                return false
            }
            return true
        },
        
        doInitialization: function (object, paras) {
            var  name  = this.initializerName();
            var _name  = this.getName();
            var value;
            var isSet  = false;
            if(typeof paras != "undefined" && typeof paras[name] != "undefined") {
                value  = paras[name];
                isSet  = true;
            } else {
                var props = this.getProps();
                
                var init  = props.init;
                
                if(typeof init == "function" && !props.lazy) {
                    value = init.call(object)
                    isSet = true
                } else {
                    if(props.required) {
                        throw "Required initialization parameter missing: "+name + "(While initializing "+object+")"
                    }
                }
            }
            if(isSet) {
                var setterName = this.setterName();
                if(object.meta.can(setterName)) { 
                    object[setterName](value)
                } else { 
                    object[_name] = value
                }
            }
        },
        
        handleHandles: function (classObject) {
            var meta  = classObject.meta;
            var name  = this.getName();
            var props = this.getProps();
            
            var handles = props.handles;
            var isa     = props.isa
            
            if(handles) {
                if(handles == "*") {
                    if(!isa) {
                        throw "I need an isa property in order to handle a class"
                    }
                    
                    var optionalHandlerMaker = props.handleWith;
                    
                    meta.decorate(isa, name, optionalHandlerMaker)
                } 
                else {
                    throw "Unsupported value for handles: "+handles
                }
                
            }
        },
        
        handlePredicate: function (classObject) {
            var meta  = classObject.meta;
            var name  = this.getName();
            var props = this.getProps();
            
            var predicate = props.predicate;
            
            var getter    = this.getterName();
            
            if(predicate) {
                meta.addMethod(predicate, function () {
                    var val = this[getter]();
                    return val ? true : false
                })
            }
        }
    }
})
})(JooseClass);

(function (Class) {

Class("Joose.Role", {
    isa: Joose.Class,
    has: ["requiresMethodNames", "methodModifiers", "metaRoles"],
    methods: {
        
        wrapMethod: function (name, wrappingStyle, func, notPresentCB) {
            this.methodModifiers.push(arguments)
            var test = this.methodModifiers
        },
        
        requiresMethod: function (methodName) {
            var bool = false;
            Joose.A.each(this.requiresMethodNames, function (name) {
                if(methodName == name) {
                    bool = true
                }
            })
            
            return bool
        },
        
        addInitializer: Joose.emptyFunction,
        
        defaultClassFunctionBody: function () {
            var f = function () {
                throw new Error("Roles may not be instantiated.")
            };
            joose.addToString(f, function () { return this.meta.className() })
            return f
        },
        
        addSuperClass: function () {
            throw new Error("Roles may not inherit from a super class.")
        },
        
        initialize: function () {
            this._name               = "Joose.Role"
            this.requiresMethodNames = [];
            this.methodModifiers     = [];
        },
        
        addRequirement: function (methodName) {
            this.requiresMethodNames.push(methodName)
        },
        
        unapply: function (object) {
            if(!joose.isInstance(object)) {
                throw new Error("You way only remove roles from instances.")
            }
            if(!object.meta.isDetached) {
                throw new Error("You may only remove roles that were applied at runtime")
            }
            
            var role  = this.getClassObject()
            
            var roles = object.meta.myRoles; 
            var found = false;
            var otherRoles = [];
            for(var i = 0; i < roles.length; i++) {
                if(roles[i] === role) {
                    found = true;
                } else {
                    otherRoles.push(roles[i])
                }
            }
            if(!found) {
                throw new Error("The role "+this.className()+" was not applied to the object at runtime")
            }
            
            var superClass     = object.meta.getSuperClass();
            var c              = superClass.meta.makeAnonSubclass();
            
            
            var test = new c()
            
            for(var i = 0; i < otherRoles.length; i++) {
                var role = otherRoles[i]
                c.meta.addRole(role)
            }
            
            c.prototype        = test
            
            object.meta        = c.meta;
            object.constructor = c;
            object.__proto__   = test
        },
        
        addMethodToClass: function (method, classObject) {
            var name = method.getName()
            var cur;
            if(method.isClassMethod()) {
                cur = classObject.meta.getClassMethodObject(name)
            } else {
                cur = classObject.meta.getMethodObject(name)
            }
            if(!cur || cur.isFromSuperClass()) {
                classObject.meta.addMethodObject(method)
            }
        },
        
        addAttributeToClass: function(attr, classObject) {
            var name = attr.getName();
            if (!classObject.meta.getAttribute(name)) {
                this.getAttribute(name).apply(classObject);
            }
        },

        apply: function (object) {
            
            if(object.meta.does(this.getClassObject())) {
                return false
            }
            
            if(joose.isInstance(object)) {
                object.detach();
                object.meta.addRole(this.getClassObject());
                this.applyMethodModifiers(object);
                var throwException = true;
                this.isImplementedBy(object, throwException)
            } else {
                var me    = this;
                var names = me.getMethodNames();
                var attrs = me.getAttributes(); 
                Joose.O.each(attrs, function applyAttrs (attr) {
                    me.addAttributeToClass(attr, object);
                });

                Joose.A.each(names, function applyMethod (name) {
                    
                    var m = me.getMethodObject(name)
                    if(m) {
                        me.addMethodToClass(m, object)
                    }
                    
                    m = me.getClassMethodObject(name)
                    if(m) {
                        me.addMethodToClass(m, object)
                    }
                })
                

                if(this.metaRoles) {
                    Joose.A.each(this.metaRoles, function applyMetaRole (role) {
                        role.meta.apply(object.meta)
                    })
                }
            }
            return true
        },
        
        applyMethodModifiers: function (object) {
            
            Joose.A.each(this.methodModifiers, function applyMethodModifier (paras) {
                object.meta.wrapMethod.apply(object.meta, paras)
            })
        },
        
        hasRequiredMethods: function (classObject, throwException) {
            var me       = this
            var complete = true
            Joose.A.each(this.requiresMethodNames, function (value) {
                var found = classObject.meta.can(value)
                if(!found) {
                    if(throwException) {
                         throw("Class "+classObject.meta.className()+" does not fully implement the role "+me.className()+". The method is "+value+" missing.")
                    }
                    complete = false
                    return
                }
            })
            return complete
        },
        
        isImplementedBy: function (classObject, throwException) {
        
            var complete = this.hasRequiredMethods(classObject, throwException);
            if(complete) {
                complete = this.implementsMyMethods(classObject);
            }
            return complete
        },
        
        handlePropmetaRoles: function (arrayOfRoles) {
            this.metaRoles = arrayOfRoles;
        }
    }
})

Joose.Role.anonymousClassCounter = 0;

})(JooseClass);

(function (Role) {
   
   var registry = {};
   var locked   = true;
   
   Role("Joose.Singleton", {
       
       before: {
           initialize: function () {
               if(locked) {
                   var name = this.meta.className()
                   throw new Error("The class "+name+" is a singleton. Please use the class method getInstance().")
               }
           }
       },
       
       methods: {
            singletonInitialize: function () {
                
            }
       },
       
       classMethods: {
           getInstance: function () {
               var name     = this.meta.className();
               var instance = registry[name];
               if(instance) {
                   return instance;
               }
               locked = false;
               instance            = this.meta.instantiate()
               locked = true;
               instance.singletonInitialize.apply(instance, arguments)
               registry[name] = instance
               return instance;
           }
       }
   })
})(JooseRole);

(function (Class) {

Class("Joose.SimpleRequest", {

    has: {_req: {}},
    methods: {
        initialize: function () {
            if (window.XMLHttpRequest) {
                this._req = new XMLHttpRequest();
            } else {
                this._req = new ActiveXObject("Microsoft.XMLHTTP");
            }
        },
        getText: function (url) {
            this._req.open("GET", url, false);
            try {
                this._req.send(null);
                if (this._req.status == 200 || this._req.status == 0)
                    return this._req.responseText;
            } catch (e) {
                throw("File not found: " + url);
                return null;
            };

            throw("File not found: " + url);
            return null;
        }
    }
})
})(JooseClass);



(function (Class) {
    
Class("Joose.Decorator", {
    meta: Joose.Role,
    methods: {
        decorate: function (classObject, attributeName, optionalDelegatorFuncMaker) {
            var me = this;
            var methods = classObject.meta.getInstanceMethods();
            Joose.A.each(methods, function (m) {
                var name    = m.getName();
                var argName = attributeName;
                if(!me.can(name)) {
                    
                    var func = function () {
                        var d = this[argName];
                        return d[name].apply(d, arguments)
                    }
                    
                    if(optionalDelegatorFuncMaker) {
                        func = optionalDelegatorFuncMaker(name)
                    }
                    
                    me.addMethod(name, func);
                }
            })
        }
    }
})

Joose.Decorator.meta.apply(Joose.Class)

})(JooseClass);

(function (Class) {

Joose.NameSpace = function () {}

Class("Joose.Module", {
    has: {
        _name: {
            is: "rw"
        },
        _elements: {
            is: "rw"
        },
        _container: {
            is: "rw"
        }
    },
    classMethods: {
        setup: function (name, functionThatCreatesClassesAndRoles) {
            var me      = this;
            var parts   = name.split(".");
            var object  = joose.top;
            var soFar   = []
            var module;
            for(var i = 0, len = parts.length; i < len; ++i) {
                var part = parts[i];
                if(part == "meta") {
                    throw "Module names may not include a part called 'meta'."
                }
                var cur = object[part];
                soFar.push(part)
                var subName = soFar.join(".")
                if(typeof cur == "undefined") {
                    object[part]      = new Joose.NameSpace();
                    module            = new Joose.Module(subName)
                    module.setContainer(object[part])
                    object[part].meta = module
                    Joose.Module._allModules.push(object[part])
                    
                } else {
                    module = cur.meta;
                    if(
                        i === (len-1) && 
                        !(module && module.meta && (module.meta.isa(Joose.Module)))) {
                        throw "Trying to setup module "+name+" failed. There is already something else: "+cur
                    }
                }
                object = object[part]
            }
            var before = joose.currentModule
            joose.currentModule = module
            if(functionThatCreatesClassesAndRoles) {
                functionThatCreatesClassesAndRoles(object);
            }
            joose.currentModule = before;
            return object
        },
        
        getAllModules: function () {
            return this._allModules
        }
    },
    methods: {
        alias: function (destination) {
            var me = this;
            
            if(arguments.length == 0) {
                return this
            }

            Joose.A.each(this.getElements(), function (thing) {
                var global        = me.globalName(thing.meta.className());
                
                if(destination[global] === thing) { 
                    return
                }
                if(typeof destination[global] != "undefined") {
                    throw "There is already something else in the spot "+global
                }
                
                destination[global] = thing;
            })
        },
        
        globalName: function (name) {
            var moduleName = this.getName();
            if(name.indexOf(moduleName) != 0) {
                throw "All things inside me should have a name that starts with "+moduleName+". Name is "+name
            }
            var rest = name.substr(moduleName.length + 1); 
            if(rest.indexOf(".") != -1) {
                throw "The things inside me should have no more dots in there name. Name is "+rest
            }
            return rest
        },
        
        removeGlobalSymbols: function () {
            Joose.A.each(this.getElements(), function (thing) {
                var global = this.globalName(thing.getName());
                delete joose.top[global]
            })
        },
        
        initialize: function (name) {
            this.setElements([])
            this.setName(name);
        },
        
        lG: function () {
            return this.getElements().length == 0
        },
        
        addElement: function (ele) {
            if(!(ele || ele.meta)) {
                throw "You may only add things that are Joose objects"
            }
            this._elements.push(ele)
        },
        
        getNames: function () {
            var names = [];
            Joose.A.each(this.getElements(), function (ele) { names.push(ele.meta.getName()) });
            return names
        }
    }
})
})(JooseClass);


__global__ = {};
__global__.meta = new Joose.Module();
__global__.meta.setName("__global__");
__global__.meta.setContainer(__global__);

Joose.Module._allModules = [__global__];

JooseModule("__global__.nomodule", function () {})
__global__.nomodule.meta._elements = joose.globalObjects;


(function (Class, Type) {

Class("Joose.TypeChecker", {
    
    classMethods: {
        makeTypeChecker: function (isa, props, thing, name) {
            if(!isa.meta) {
                throw new Error("Isa declarations in attribute declarations must be Joose classes, roles or type constraints")
            }
        
            var isRole  = false;
            var isType  = false;
            if(Joose.Role && isa.meta.meta.isa(Joose.Role)) {
                isRole  = true;
            } 
            else if(Joose.TypeConstraint && isa.meta.isa(Joose.TypeConstraint)) {
                isType  = true;
            }
            
            func = function doTypeCheck (value, errorHandler) {
                try {
                    if ( props.nullable === true && value == undefined) {
                        } else if ( isType ) {
                        var newvalue = null;
                        if( props.coerce ) {
                            newvalue = isa.coerce(value);
                        }
                        if ( newvalue == null && props.nullable !== true) {
                            isa.validate(value);
                        } else {
                            value = newvalue;
                        }
                    } else {
                        if(!value || !value.meta) {
                            throw new ReferenceError("The "+thing+" "+name+" only accepts values that have a meta object.")
                        }
                        var typeCheck = isRole ? value.meta.does(isa) : value.meta.isa(isa);
                        if( ! typeCheck ) {
                            throw new ReferenceError("The "+thing+" "+name+" only accepts values that are objects of type "+isa.meta.className()+".")
                        }
                    }
                } catch (e) {
                    if(errorHandler) {
                        errorHandler.call(this, e, isa)
                    } else {
                        throw e
                    }
                };
                return value
            }
            
            return func
        }
    }
})

})(JooseClass, JooseType);

(function (Class) {

Class("Joose.TypeConstraint", {
    has: {
        _constraints: {
            is: "ro",
            init: function () { return [] }
        },
        _coercions: {
            is: "ro",
            init: function () { return [] }
        },
        _messages: {
            is: "ro",
            init: function () { return [] }
        },
        _callback: {
            is: "ro",
            init: function() {
                return function (msg) {
                    throw new ReferenceError(msg);
                };
            }
        },
        _name: {
            is: "ro"
        },
        _uses: {
            is: "ro"
        },
        props: {
            is: "rw"
        }
    },
    
    classMethods: {
    	newFromTypeBuilder: function (name, props) {
            var t = new Joose.TypeConstraint({ name: name });
            if ( props.uses 
                 && typeof props.uses.meta != 'undefined'
                 && props.uses.meta.isa(Joose.TypeConstraint) ) {
                 t._uses = props.uses;
            }

            if(props.where) {
                t.addConstraint(props.where, props.message)
            }

            t.setProps(props)
            
            if(props.coerce) {
                for(var i = 0; i < props.coerce.length; i++) {
                    var coercionProps = props.coerce[i];
                    t.addCoercion(new Joose.TypeCoercion({
                        from: coercionProps.from,
                        via:  coercionProps.via
                    }))
                }
            }
            
            return t
        }
    },
    
    methods: {
        
        stringify: function () {
            return this._name
        },
        
        makeSubType: function (name) {
            var t = new Joose.TypeConstraint({ name: name })
            Joose.A.each(this._constraints, function (con) {
                t.addConstraint(con)
            })
            return t
        },
        
        addCoercion: function (coercion) {
            this._coercions.push(coercion);
        },
        
        addConstraint: function (func, message) {
            this._constraints.push(func);
            this._messages.push(message)
        },
        
        getConstraintList: function () {
            var cons = this._constraints;
            if ( this._uses ) {
                var parentcons = this._uses.getConstraintList();
                return parentcons.concat(cons);
            }
            return cons;
        },
        
        getMessageList: function () {
            var msg = this._messages;
            if ( this._uses ) {
                var parentmsg = this._uses.getMessageList();
                return parentmsg.concat(msg);
            }
            return msg;
        },

        validateBool: function (value) {
            var i = this._validate(value);
            if(i == -1) {
                return true
            }
            return false
        },
        
        validate: function (value) {
            var i = this._validate(value);
            if(i == -1) {
                return true
            }
            var messages = this.getMessageList();
            var message = messages[i] 
                ? messages[i].call(this,value)
                : "The passed value ["+value+"] is not a "+this;
            this._callback(message);
        },
        
        _validate: function (value) {
            var con = this.getConstraintList();
            var i, len;
            for(i = 0, len = con.length; i < len; i++) {
                var func = con[i];
                var result = false;
                if(func instanceof RegExp) {
                    result = func.test(value)
                } else {
                    result = func.call(this, value)
                }
                
                if(!result) {
                    return i
                    
                }
            }
            return -1
        },

        coerce: function (value) {
            if(this.validateBool(value)) {
                return value
            }
            var coercions = this._coercions;
            for(var i = 0, len = coercions.length; i < len; i++) {
                var coercion = coercions[i];
                var result   = coercion.coerce(value);
                if(result !== null) {
                    return result
                }
            }
            return null
        }
    }
});

})(JooseClass);

(function (Class, Type) {

Type('CoercionFrom', {
    where: function(o) {
        if ( o.meta && o.meta.isa(Joose.TypeConstraint) ) {
            return true;
        }
        return false;
    }
});

Class("Joose.TypeCoercion", {
    has: {
        _from: {
            isa: TYPE.CoercionFrom,
            is:  "rw"
        },
        _via: {
            is: "rw"
        }
    },
    
    methods: {
        coerce: function (value) {
            if(this._from.validateBool(value)) {
                return this._via(value)
            }
            return null
        }
    }
})

})(JooseClass, JooseType);

(function (Type) {
	Type('Any', {
	    where: function(o) {
			return true
	    }
	});


	Type('Null', {
	    uses: Joose.Type.Any,
	    where: function(o) {
	        if (o === null) {
	            return true;
	        }
	        return false;
	    }
	});
	
	Type('NotNull', {
	    uses: Joose.Type.Any,
	    where: function(o) {
	        if (o === null) {
	            return false;
	        }
	        return true;
	    }
	});

	Type('Enum', {
	    uses: Joose.Type.NotNull,
	    message: function(v) {
	        return "The passed value ["+v+"] is not "+
	               (this.getProps().strictMatch?"*strictly* ":"")+
	               "one of ["+this.getProps().values.join(",")+"]";
	    },
	    where: function (v) {
	        var self  = this;
	        var props  = self.getProps()
	        if ( !props || props.values === undefined || !(props.values instanceof Array)) {
	            throw "Enum Type needs Array of values in 'values' property of Type declaration"
	        }
	        var eq = function(vv) {
	            if (props.strictMatch === true) return (vv === v);
	            return (vv == v);
	        }
	        if ( Joose.A.grep(props.values, eq).length != 0 ) {
	            return true;
	        }
	        return false;
	    }
	});

	Type('Obj', {
	    uses: Joose.Type.NotNull,
	    where: function (o) {
	        if ( o instanceof Object ) {
	            return true;
	        }
	        return false;
	    }
	});

	Type('Str', {
	    uses: Joose.Type.NotNull,
	    where: function(S) {
	        if ( typeof S == 'string' || S instanceof String ) {
	            return true;
	        }
	        return false
	    },
	    coerce: [{
	        from: Joose.Type.Any,
	        via:  function (value) {
	            if(value == null) {
	                return ""
	            } else {
	                return "" + value
	            }
	        }
	    }]
	});

	Type('Num', {
	    uses: Joose.Type.NotNull,
	    where: function(N) {
	        if ( typeof N == 'number' || N instanceof Number ) {
	            return true;
	        }
	        return false
	    },
	    coerce: [{
	        from: Joose.Type.Str,
	        via:  function (value) {
	            if(value == null || value == "") return undefined;
	            return parseFloat(value, 10)
	        }
	    }]
	});

	Type('Bool', {
	    uses: Joose.Type.NotNull,
	    where: function(B) {
	        if (B === true || B === false) {
	            return true;
	        }
	        return false;
	    },
	    coerce: [{
	        from: Joose.Type.Any,
	        via:  function (value) {
	            if(value == null || value === "") return false;
	            if(value == 1 || value == "1" || value == "true") {
	                return true
	            }
	            if(value == 0 || value == "0" || value == "false" ) {
	                return false
	            }
	            return null
	        }
	    }]
	});

	Type('Int', {
	    uses: Joose.Type.Num,
	    where: function(n) {
	        var sn = String(n);
	        if ( sn.match(/^\d*\.\d$/) ) {
	            return false;
	        }
	        return true;
	    },
	    coerce: [{
	        from: Joose.Type.Str,
	        via:  function (value) {
	            if(value == null || value == "") return undefined;
	            if(value.match(/^-{0,1}\d+$/)) {
	                return parseInt(value, 10)
	            }
	            return
	        }
	    }]
	});

	Type('Float', {
	    uses: Joose.Type.Num,
	    where: function(n) {
	        return true
	    }
	});

	Type('Func', {
	    uses: Joose.Type.Obj,
	    where: function (f) {
	        if ( typeof f == 'function' ) {
	            return true;
	        }
	        return false;
	    }
	});

	Type('Array', {
	    uses: Joose.Type.Obj,
	    where: function (A) {
	        if ( Object.prototype.toString.call(A) === '[object Array]' ) {
	            return true;
	        }
	        return false;
	    }
	});

	Type('Date', {
	    uses: Joose.Type.Obj,
	    where: function (D) {
	        if ( D instanceof Date ) {
	            return true;
	        }
	        return false;
	    },
	    coerce: [{
	        from: Joose.Type.Str,
	        via:  function (value) {
	            var match;
	            if(value == undefined || value == "") {
	                return undefined;
	            } else if(match = value.match(/\s*(\d+)-(\d+)-(\d+)/)) {
	                return new Date(match[1], match[2]-1, [match[3]])
	            }
	            return null
	        }
	    }]
	});

	Type('Joose', {
	    uses: Joose.Type.Obj,
	    where: function (o) {
	        if ( o.meta && o.meta.meta.isa(Joose.Class) ) {
	            return true;
	        }
	        return false;
	    }
	});	
})(JooseType);

(function (Class) {

Class("Joose.Prototype", {
    isa: Joose.Class,
    override: {
        initializer: function () {
            var init = this.SUPER()
            return function () {
                init.apply(this, arguments)
                var meta = this.meta;
                this.meta = new Joose.PrototypeLazyMetaObjectProxy();
                this.meta.metaObject = meta
                this.meta.object     = this;
            }
        }
    }
})


Class("Joose.PrototypeLazyMetaObjectProxy", {
    has: {
        metaObject: {
            is: "rw",
            isa: Joose.Class,
            handles: "*",
            handleWith: function (name) {
                return function () { 
                    var o = this.object;
                    o.meta = this.metaObject;
                    o.detach() 
                    o.meta[name].apply(o.meta, arguments)
                }
            }
        },
        object: {
            is: "rw"
        }
    }
})

Joose.bootstrap3()

})(JooseClass);

(function (Class, Type) {

Class("Joose.TypedMethod", {
    isa: Joose.Method,
    
    has: {
        types: {
            isa: Joose.Type.Array,
            is:  "rw",
            init: function () { return [] }
        },
        
        typeCheckers: {
            init: function () { return [] }
        }
    },
    
    after: {
        setTypes: function () {
            var self         = this;
            var typeCheckers = [];
            var props        = this.getProps();
            
            Joose.A.each(this.getTypes(), function (type, index) {
                if(type === null) {
                    typeCheckers.push(null)
                } else {
                    typeCheckers.push(Joose.TypeChecker.makeTypeChecker(type, props, "parameter", index))
                }
            })
            
            this.typeCheckers = typeCheckers
        }
    },
    
    override: {
        copy: function () {
            var self = this.SUPER();
            var copy = [].concat(this.types)
            self.setTypes( copy ); 
            return self;
        }
    },
    
    methods: {
        
        wrapTypeChecker: function(body) {
            var self = this;
            return function typeCheckWrapper () {
                var checkers = self.typeCheckers;
                var args = [];
                for(var i = 0, len = checkers.length; i < len; ++i) {
                    var checker = checkers[i]
                    if(checker !== null) {
                        var argument = arguments[i]
                        args[i]      = checker(argument)
                    } 
                    else {
                        args[i]      = arguments[i]
                    }
                }
                return body.apply(this, args)
            }
        },
        
        asFunction: function () {
            return this.wrapTypeChecker(this._body)
        }
    },
    
    classMethods: {
        newFromProps: function (name, props) {
            var method = props.method;
            if(typeof method !== "function") {
                throw new Error("Property method in method declaration ["+name+"] must be a function.")
            }
            var self   = this.meta.instantiate(name, method, props);
            self.setTypes(props.signature);
            return self;
        }
    }

})

})(JooseClass, JooseType);

Module('Joose.Type', function() {
    Type('MethodPatternList', {
        uses: Joose.Type.Array,
        where: function(p) {
            var ok = 0;
            for (var i in p) {
                var pattern = p[i];
                if (pattern.signature instanceof Array
                    && typeof pattern.method == 'function') {
                    ok++;
                }
            }
            return p.length == ok;
        }
    });
});

Class('Joose.MultiMethod', {
    isa: Joose.Method,
    
    has: {
        patterns: {
            is: 'rw',
            isa: Joose.Type.MethodPatternList,
            init: function() { return [] }
        }
    },
   
    override: {
        copy: function() {
            var self = this.SUPER();
            var patternCopy = [].concat(this.getPatterns());
            self.setPatterns( patternCopy );
            return self;
        }
    },

    methods: {
        getFunForSignature: function() {
            var args = arguments;
            var self = this;
            var patterns = self.getPatterns();
            for (var item in patterns) {
                if(patterns.hasOwnProperty(item)) {
                    var method = patterns[item];
                    var sig = method.signature;
                    var matches = 0;
                    if (sig.length == args.length) {
                        if (sig.length > 0) {
                            for (var i=0; i < sig.length; i++) {
                                if (sig[i] instanceof Joose.TypeConstraint
                                    && sig[i].validateBool(args[i])) {
                                        matches++;
                                } else if (sig[i] instanceof Object 
                                    && args[i] instanceof sig[i]) {
                                        matches++;
                                } else if (args[i] == sig[i]) {
                                    matches++;
                                }
                            }
                        }
                        if (matches == sig.length)
                            return method.method;
                    }
                }
            }
            return function () {
                    throw new ReferenceError("multi-method type method call " 
                        +"with no matching signature");
                };
        },
        asFunction: function() {
            var self = this;
            return function() {
                var myself = this;
                var args = arguments;
                var fun = self.getFunForSignature.apply(self, args);
                return fun.apply(myself, args);
            }
        }
    },
    classMethods: {
        newFromPatterns: function(name, patterns) {
            method = new Joose.MultiMethod(name, function() {}, {});
            method.setPatterns(patterns);
            return method;
        }
    }
});
var app,a;							
	var appCnt;							
	var c;								
var jg;								
	var tr,t;							
	var tc;								
	var stack;							
	var err;							
	var tinyMCE;						
	var debug = false;					
	var E = Ext;						
	var t25 = true;	
function L(){
		this.coll = Array();				
		this.count = 0;						
		}

L.prototype.add = function(obj){
		this.count = this.count + 1;
		this.coll[this.count] = obj;
		return this.count;
		};

L.prototype.addMenu = function(obj,bc){
		if(G(bc)) {bc = true;}
		if(bc) {this.add(obj);}
		};


L.prototype.addAll = function(anArray){
for (i in anArray) {this.add(anArray[i]);}		
		return this.count;
		};

L.prototype.addAllOc = function(oc){
		for (i=1;i< oc.count + 1 ;i++ )
			{this.add(oc.at(i));}
		return this.count;
		};

L.prototype.bo = function(obj){
		if(!G(obj)) {this.add(obj);}
		return this.count;
		};

L.prototype.at = function(anInt){
		return this.coll[anInt];
		};

L.prototype.cV = function(aKey,anObj){

		this.coll[aKey]=anObj;
		this.count = this.count + 1;
		return anObj;
		};

L.prototype.size = function(){
		return this.count;
		};

L.prototype.last = function(){
		return this.at(this.count);
		};

L.prototype.asString = function(){
		var g = c.cC;
		if(this.count < 1) {return g;}
		for (i=1 ;i < this.size() + 1 ;i++){
			g = g + this.coll[i];}
		return g;
		};

L.prototype.asJsArray = function(){
		var r = [];
		if(this.count < 1) {return r;}
		for (i=1 ;i < this.size() + 1 ;i++){
			r[i-1] = this.at(i);}
		return r;
		};

L.prototype.copyNonEmptyElements = function(){
		var oc = new L;
		if(this.count < 1) {return oc;}
		for (i=1 ;i < this.size() + 1 ;i++){
			if(!F(this.at(i)))
				{oc.cV(i,this.at(i));}
		} 
		return oc;
		};

	stack = new L();
	err = new L();

function F(o){
		return (o == null);
		}

function G(o){
if(F(o)) {return true;}
if(mw(o)) {return true;}
if(E.isBoolean(o)) {return false;}
if(isZero(o)) {return true;}									
		if(E.isArray(o)) {if(isZero(o.length)) {return true;}}			
		return false;
		}

	function mw(o){
		if(isZero(o)){return false;}									
		if(o == ' '){	return true;}
		return false;
		}

	function isZero(o){
		return (o == 0);
		}

	function aa(g){
		return "'" + g + "'";
		}

	function tX(g){
		return "(" + aa(g) + ")";
		}

	function ab(g){
		return '"' + g + '"';
		}

function eo(g){
		return "<b>" + g + "</b>";
		}




function bC(anObject, ck, arg){
		var code = anObject+ck+'='+arg+';';
		if(cH(arg))								
			{eval(code);}
		}

	function cH (o){
		if(F(o)) {return false;}								
		if(mw(o)) {return false;}								
		if(o == 'nop') {return false;}								
		return true;}



function au(){
		this.tt_htmlCr = '<br>';
		this.languageForUI;
		this.tr1 = '<p></p><a target="_blank" href="';
		this.tr2 = '">? - </a><b>';
		this.tr3 = '</b>';
		this.trH3a = '</h1><b>';
		this.trH3z = '</b></h1>';
	}

au.prototype.initialize = function(){
	};

au.prototype.tt = function(o){
		if(c.bT == 2) {return null;}
		return eval('t.' + o);
	};

	au.prototype.wA = function(){
	if(!F(ecLanguageRequested)) {
			if(c.uX(ecLanguageRequested))
				{this.languageForUI = ecLanguageRequested.substr(0,2);
c.uy('in trans this.languageForUI '+ this.languageForUI);
				return this.languageForUI;} 
			} 
	this.languageForUI = 'en';			
		if(G(ecLanguageBrowser)) {ecLanguageBrowser = 'en';}
		var lang = ecLanguageBrowser.substr(0,2);
		if(lang == 'de') {this.languageForUI = 'de';}
		if(lang == 'en') {this.languageForUI = 'en';}
		if(lang == 'fr') {this.languageForUI = 'en';}
		c.initializeLanguageTranslation(this.languageForUI);	
		return this.languageForUI;
	};
au.prototype.translateIfNeeded = function(o){
		var pos = o.substr(1,2).indexOf('_');
		if(pos < 1) {return o;}
return eval('tr.' + o);
	};

	au.prototype.aU = function(o){
		var text = eval('this.'+o);
		alert(text);
	};

au.prototype.qX = function(o){
		alert(o);
	};


au.prototype.hb = function(o){
	
	};



	au.prototype.mr = function(text){
		var ans = confirm(text);
		if(true) {return ans;}
		return false; 
	};


au.prototype.openDialogYesNoResult = function(o){
		if(o == 'cancel') {return false;}
		if(o == 'no') {return false;}
		return true;
    };

	au.prototype.alertObsolete = function(){
		alert('This is marked as obsolete code');
		debugger;
	};

	au.prototype.tH = function(o){

	};

au.prototype.zc = function(){
		return (this.languageForUI == 'de');
	};

au.prototype.zh = function(){
		return (this.languageForUI == 'en');
	};

tr = new au;
	tr.initialize();
t = new au;
	t.initialize();

function cl(o){
		UnTip();			
		a.bb(o);
	}

function clGallery(o){
		a.rB(o.id);
	}

function clIconCat(o){
		t.alertObsolete(); 
}

	function fd(aGrid,aRow,anEvent){
		return a.fd(aGrid,aRow,anEvent);}


function checkBoxSet(checkBoxObj){
var yX = checkBoxObj.checked;
}

function resize(sender, width, height, mouse){
}


if(ecLanguageRequested.substr(0,2) == "de") {				
	au.prototype.dO = function(){
	this.fL = 'Seite ';		
	this.hG = 'Hilfe';
this.pO = 'Elecat';

	this.ij = 'Firmen';
	this.hZ = 'http://www.elecat.com/help/EN/lhAccCompanies';
	this.ih = 'Kataloge';
	this.ik = 'http://www.elecat.com/help/EN/lhAccCatalogs';
	this.ib = 'Seiten-Titel';
	this.hA = 'http://www.elecat.com/help/EN/lhAccPagesTitles';
	this.ia = 'Seiten-Ikonen';
	this.ic = 'http://www.elecat.com/help/EN/lhAccPagesIcons';
	this.hB = 'Einstellungen';
	this.hD = 'http://www.elecat.com/help/EN/lhAccSettings';
	this.ie = 'Warenkorb';
	this.fV = 'http://www.elecat.com/help/EN/lhBasket';
	this.hY	= 'Laden von Daten';

this.hI = 'Heim';
	this.iq = 'Heimseite, Willkommen und Einführung';

	this.hu = 'Kataloge';		
	this.iy = 'Ikonen-Ansichten und eine detaillierte Liste der Kataloge und Seiten';

	this.fG = 'Daten';			
	this.fy = 'Details-Daten, in Katalogen ausgewählt, aus der Datenbank';  
	this.oJ = 'Flash';
this.oG = 'Anfragen';
	this.pG = 'Anfragen und Aufträge eingeben und bearbeiten';

this.hC = 'Katalog Ikonen';
	this.iu = 'Ikonen von Produkt-Katalogen - mit den +/- Knöpfen zum Zoomen';

	this.hv = 'Katalog-Liste';
	this.iw = 'Detailierte Liste von Produkt-Katalogen';

	this.qb = 'Seiten';
	this.pE = 'Ikonen und detaillierte Liste von Seiten - mit den +/- Knöpfen zum Zoomen';

	this.hz = 'Katalog-Galerie - Ikonen von Produkt-Katalogen';
	this.fX = 'Detaillierte Liste von Produkt-Katalogen (wie im linken Teil)';

	this.ph = 'Seten-Galerie - Ikonen der Seiten des aktuellen Produkt-Katalogs';
	this.oS = 'Detaillierte Liste der Seiten des aktuellen Produkt-Katalogs (wie im linken Teil)';

this.iS = 'Elecat Detail-Fenster -> ';
	this.iU = 'Elecat Bild-Galerie -> ';
	this.iV = 'Elecat Gelbe Zettel Fenster';
	this.iZ = 'Elecat Video Fenster';
	this.iR = 'Elecat Text-Editor';

this.iM = 'Werkzeuge';
	this.nc = 'http://www.elecat.com/help/EN/lhTools';

	this.jh = 'Elecat Reader - E-Papier Produkt-Katalog System';
	this.jf = 'http://www.elecat.com';
	this.ji = 'Daten-Navigation';
	this.jk = '';
	this.jj = 'Details des gewählten Objektes in der Seite';
	this.pf = '';
	this.jb = 'Dieser Bereich enthält Details des in einer Seite ausgewählten Produktes';

this.mR = 'Zeigt die Online Elecat Hilfe';
	this.iz = 'Zeigt die Online Elecat Hilfe';
	this.iB = 'Katalog: ';
	this.iD = 'Herausgeber: ';
	this.ej = 'Seite Nr: ';
	this.eh = 'Titel: ';
	this.iC = 'Bild: ';

	this.tt_ynNote = 'Note';

this.iQ = 'Seiten in neuem Register';
	this.iK = 'Öffnen im Klick-Modus';
	this.iO = 'Mit Hilfe';
	this.nJ = 'Zoom-Schritt';
	this.iP = 'Zoom Vorschlag';
	this.nD = 'Zoom';

	this.jF	= 'Anfänger';
	this.jH	= 'Fortgeschritten';
	this.jJ		= 'Experte';

	this.oa = 'Seiten in neuem Register';
	this.od = '';
	this.oi = '';
	this.mZ = '';

	this.kF = 'Nächste Seite';
	this.kH = 'Klick-Modus / Bewegen-Modus';
	this.nY = 'Vorige Seite in der Legende';
	this.oh = 'Nächste Seite in der Legende';
	this.kJ = 'Actual page size';				
	this.kI = 'Fit to page (show entire page)';
	this.kh = 'Fit to width (use full width)';
	this.kg = 'Neue Seiten in neuen Tabs';
	this.ki = 'Neuen Tab öffnen';
	this.kk = 'Suche';
	this.kj = 'Vorige Seite';
	this.fk = 'Zoom vergrößern';
	this.mW = 'Zoom in %';
	this.fv = 'Zoom verkleinern';

this.eY		= 'Öffnet spezielle Seiten des gewählten Katalogs';
	this.ob			= 'Öffnet ein Fenster auf die Daten';				
	this.eZ		= 'Zeigt Produkt-Listen in einer Detail-Ansicht';
	this.eU		= 'Kontaktieren Sie den Anbieter';
	this.fa		= 'Anfrage eingeben / Fragen stellen';
	this.fb			= 'Öffnet die Hilfe-Seiten';
	this.eT		= 'Informationen und Verweise auf Websites zu den Katalogen und der Elecat Software';

this.kE				= 'Schließen';

	this.dJ				= 'Elecat Information zu Releasen';
	this.hX				= 'Elecat Browser-Information';
	this.fK			= 'Elecat Benutzer-Information';

	this.t_buttonNoteTab1				= 'Benutzer-Information';
	this.t_buttonNoteTab2				= 'Mehr Details';

	this.kq = 'Diese Seiten hat keine Bereiche zum Anklicken';
	this.kr = 'Der Zoom-Faktor ist zu klein, um Boxen zu zeichnen';
	this.ke = 'Dieses Register kann nicht geschlossen werden';

this.oo			= 'Diese Eigenschaft ist noch nicht verfügbar!';
	this.oq 			= 'Diese Eigenschaft ist kein Teil dieser Version des Elecat Readers!';
	this.kb 			= 'Auf dieser Seite gibt es keine zusätzlichen Bilder!';

this.kz				= 'Seiten';
	this.nG				= 'Ansicht';			
	this.ky			= 'Produkte';
	this.kw			= 'Kontakt';
	this.kv			= 'Anfrage';
	this.kx				= 'Hilfe';
	this.kD				= 'Über';
this.ka 			= 'Inhaltsverzeichnis';
	this.jz 			= 'Index';
	this.jv 		= 'Glossar';
	this.jx 		= 'Impressum';
	this.jD 		= 'Kontakt-Daten';
	this.jw 		= 'Händler';
	this.jy 	= 'Links auf Seiten Zeigen';
	this.tl 	= 'Dieser Katalog enthält keine Seite mit diesem Inhalt.';
this.kC 		= 'Produkte auf dieser Seite';
	this.kA 		= 'Alle Produkte in diesem Katalog';
	this.kB 			= 'Verfügbarkeit / Bestand';
this.jB	= 'Allgemeine Text-Nachricht';
	this.jo 		= 'Formelle Anfrage';
	this.jp 		= 'Auftrag senden';
this.jt 	= 'Telefon-Kontakt anfordern';
	this.jm 	= 'Nachricht per Elecat senden';
	this.jl 	= 'Anfrage-Text senden';
	this.ju 		= 'Email schreiben';
this.jC 		= 'Hilfe Haupt-Seite';
	this.jE 		= 'Hilfe zum Elecat Reader';
	this.jA 	= 'Hilfe zum Elecat Generator';
this.js 		= 'Über diese Website';
	this.jq 		= 'Über den Herausgeber';
	this.jT 		= 'Über diesen Katalog';
	this.jr 		= 'Über Elecat';
	this.jn 		= 'Debug mode on';

this.kL 					= 'Sprache wählen';
	this.ks = 'Sprache des Benutzer-Interfaces:';
	this.kt = 'Die Sprache des Benutzer-Interfaces wurde nicht verändert';
	this.ku = 'Wollen Sie die Sprache des Benuter-Interfaces ändern?';

	this.eI		= 'Auswahl Experten-Status';	
	this.kl = 'Keine Änderung';
	this.km = 'Wollen Sie den Experten-Status ändern?\nDafür muß die Anwendung neu geladen werden.';

this.kK 					= 'Dieses Register schließen';
	this.kM 					= 'Andere Register schließen';

this.eH 		= 'Artikel';
	this.mP 		= 'Öffnet ein Fenster mit einer Liste aller Produkte<br>auf dieser Katalog-Seite';
	this.gx			= 'Bilder';
	this.np			= 'Öffnet ein Fenster mit allen Bildern<br>auf dieser Katalog-Seite';
	this.gG	 		= 'Preise';
	this.nm	 		= 'Öffnet ein Fenster mit allen Preisen aller Produkte<br>auf dieser Katalog-Seite';
	this.gE	 		= 'Bestand';
	this.nn	 		= 'Abfrage des Bestands oder der Verfügbarkeit vom Server für die Produkte<br>auf dieser Katalog-Seite';
	this.gB	 		= 'Warenkorb';
	this.mQ	 		= 'Öffnet den Warenkorb zur Datenpflege (hinzufügen, löschen, ansehen)';
	this.cY		= 'Gefüllt durch die Seite';

this.kn	 		= 'Es gibt keine weiteren Bilder für diese Seite';
	this.kp	 		= 'Preise sind nur in der Elecat Server Version verfügbar';
	this.ko	 		= 'Bestände und Verfügbarkeiten sind nur<br>in der Elecat Server Version verfügbar';

this.cq 			= 'Einfügen in Tab';
	this.mL	 		= 'Das aktuelle Detail-Fenster wird zum Daten-Tab<br>permanent hinzugefügt';
	this.eE	 		= 'Anfrage';
	this.mK	 		= 'Open thew form for entering an enquiry';
	this.eP	 		= 'Warenkorb';
	this.mJ	 	= 'Add this article to your basket';
	this.lO 		= 'Favoriten';
	this.mO 	= 'Add this article to your favorites';
	this.eO	 		= 'Bestand';
	this.mI	 		= 'Get stock or availability data for this product<br>from the server';
	this.lS	 		= 'Anbieter';
	this.aC	 		= 'Schließen';
	this.gl	 		= 'Schließen Sie dieses Fenster';
this.eQ		= this.aC;
	this.mm_tbMenuECRDetailsCloseEditor		= this.gl;

this.bR 		= 'Nachricht senden';

this.eG 			= 'Voriges';
	this.eA		 		= 'Nächstes';
	this.lV	 		= 'Maximum';

this.hO			= 'http://www.elecat.com/help/EN/lhBasket';
	this.pV			= 'http://www.elecat.com/help/EN/lhBasket';
	this.hS		= 'http://www.elecat.com/help/EN/lhBasket';
	this.ig	= 'http://www.elecat.com/help/EN/lhBasket';

this.il	= 'Bilder Galerie';
	this.hH			= 'Bild';
	this.gJ			= 'Details';

	this.pF			= 'Die gewählte Seite';
	this.it			= 'Weitere Details dieses Bildes, wenn verfügbar';


this.fP			= 'Info';
	this.fR			= 'Absender';
	this.pk		= 'Empfänger';
	this.hE			= 'Text';
	this.pi			= 'Senden';

	this.ix			= 'Some basic information about how to use this enquiry form';
	this.ip		= 'Please add your contact details in this form';
	this.qc		= 'Here you select the receiver(s) of your enquiry';
	this.im			= 'Enter your enquiry text (see info tab)';
	this.pu			= 'Send your enquiry to the selected receiver(s)';

this.oR			= 'Texts to use';
	this.hP				= 'Info';
	this.hW				= 'Warenkorb';
	this.hQ		= 'Text-Module';
	this.hR		= 'Fragen';

this.hw 	= 'Details';
	this.pg 	= 'Eine HTML-Seite mit weiteren Produkt-Details';
	this.hy 	= 'Details2';
	this.oD 	= 'Eine weitere HTML-Seite mit weiteren Produkt-Details<br>(optional) generiert aus der Datenbank';
	this.hx 	= 'Details3';
	this.oz 	= 'Eine dritte Html-Seite, wenn gewünscht, mit zusätzlichen<br>Produkt-Details, generiert aus der Elecat-Datenbank';
	this.hJ	= 'Bilder';
	this.pn 	= 'Weitere Bilder des gewählten Produktes in einer HTML-Seite';
	this.hM = 'Technisch';
	this.qd = 'Hier wird eine Html-Seite mit technischen Informationen<br>dargestellt und diese Seite wird ebenfalls<br>aus der Elecat-Datenbanl generiert';
	this.hN	= 'Verwandte Produkte';
	this.pW	= 'Zeigt eine Html-Seiten mit anderen Produkten,<br>die mit diesem hier in irgendeinem Zusammenhang<br>stehen wie Zubehör, Ersatzteile, Alternativen etc';
	this.hL	= 'Preise';
	this.pM	= 'Zeigt Preis-Daten für dieses Produkt, die vom Server geladen werden';
	this.hK		= 'Auftrag';
	this.oB		= 'Öffnet die Auftrags-Erfassung zur Eingabe<br>von Anfragen und/oder Aufträgen';
	this.hF	= 'Gelbe Zettel';
	
	this.or 		= 'Über Elecat';

	this.n_infoFinalRelease = 'Dies wird im endgültigen Elecat Release funktionieren.';
	this.n_infoNextRelease = 'Dies wird Teil des nächsten Elecat Releases.';
	this.n_infoNextReleaseServer = 'Dies wird Teil der Elecat Server Version,<br>die Teil des nächsten Releases ist.';


this.kd						= 'Die Elecat Software wird initialisiert';
	this.kf					= 'Initialisierung der Elecat Software in Arbeit...';
	this.nd							= 'Es gibt ein Problem in: ';
	this.ft		= 'Please enter some keyword(s)!';
	this.fA		= 'Kein Ergebnis verfügbar';
	this.hT		= 'Das Maximum von XX neues Tabs wurde erreicht';
	this.gL		= 'Laden.....bitte warten.....';
	this.hU			= 'Keine Seite gewählt';

this.hV				= 'Da dies eine Demo-Version ist, wurde nichts wirklich gesendet!';

this.jS					= 'Elecat Such-Fenster';

this.je				= 'Suche';
	this.iE				= 'Neue Suche: Suche in HTML und Katalog-Seiten';
	this.jc				= 'Erweiterte Suche';
	this.iF			= 'Die erweiterte Suche bietet zusätliche Such-Kriterien';

	this.jd				= 'Datenbank-Suche';
	this.iG			= 'Suche in der Server-Datenbank (in der Hilfe finden Sie weitere Erklärungen)';

	this.pd			= 'Letztes Ergebnis';
	this.nq			= 'Enthält das Ergebnis der vorherigen Suche';

	this.t_searchFormTopTabResultNrXXX			= 'Suchergebnis XXX';
	this.iJ			= 'Enthält ein früheres Suchergebnis; Sie können das Tab mit dem (x) Knopf schließen';


	this.ja				= 'Suche nach';
	this.ir				= 'IS this shown ';

	this.sA				= 'Suchergebnisse';
this.jU		= 'Noch kein Suchergebnis verfügbar';	
	this.l_searchResultPanelHtmlNothingFound 	= 'Nichts gefunden! Bitte geben Sie andere Suchbegriffe ein';	
	this.t_searchResultPreviewXXX				= 'Voransicht XXX';						
	this.iI					= 'Voransicht der gefundenen Seite.<br>Klicken Sie auf die Seite,um diese im<br>Haupt-Fenster zu öffnen';	

	this.iv			= 'Voransicht XXX';					
	this.iA		= 'Press "Page to tab" in toolbar to open<br>this page in a larger "data" tab';

this.cS					= 'Letztes Suchergebnis';			
	this.t_searchBasicTabResultXXX				= 'Suchergebnis #XXX';				
	this.fn				= 'This is the result of the last search; to remember, press the button "Store results".';
this.eu			= 'Suchergebnis Voransichten';	
	this.fz		= 'This is useful for a quick selection of search results, which can later be viewed in a larger data tab. Press "Result to tab".';

	this.ou			= 'Neue Suche';						
	this.na			= 'Clear the search field and the last search result';
	this.ov		= 'Suchen';
	this.mX		= 'Perform a search for the entered keywords';
	this.ot			= 'Ergebnisse speichern';
	this.mY		= 'Remember last search result, move to a new tab';

	this.jI				= 'Suche in';
	this.jW				= 'Ganze Website';
	this.jV				= 'Nur Katalog-Seiten';
	this.jX				= 'Nur Artikel-Seiten';

this.jP				= 'Art der Suche';
	this.jY					= 'Jedes';
	this.jZ					= 'Alle';
	this.jR				= 'Exakt';


this.gq 			= 'Suchergebnis in Tab';
	this.nz 			= 'Fügen Sie die aktuelle Ergebnis-Seite hier oben hinzu<br>(hier in diesem Suchfenster), für spätere Nutzung<br>';
	this.gn 		= 'Alle Suchergebnisse in Tabs    - '; 
	this.nu 		= 'Fügen Sie bis zu 10 Ergebnis-Seiten hier hinzu<br>(hier oben) für spätere Nutzung';

	this.cP 			= 'Seite anzeigen';
	this.nt 			= 'Adds current page from preview to a data tab<br>(in the main tabs "Data") with more space.<br>Checkbox "Result preview" must be enabled<br>and a preview page selected.';
	this.gC 			= 'Alle Seiten anzeigen';
	this.ny			= 'Öffnet alle Ergebnisse der gewählten Ergebnis-Seite\nals Voransicht oder unten im großen Haut-Tab\nabhängig von der Checkbox "Voransichten"';

	this.dW 		= 'Voransichten';
	this.kN 	= 'Voransichten werden verwendet! Das heißt:\nWenn Sie im Suchergebnis auf einen Link klicken,\nöffnet diese Seite hier im Suchfenster in Voransichten';
	this.kO	= 'Voransichten werden NICHT verwendet! Das heißt:\nWenn Sie im Suchergebnis auf einen Link klicken,\nöffnet diese Seite im großen Haupt-Fenster';
	};
	}

if(ecLanguageRequested.substr(0,2) == "en") {				
	au.prototype.dO = function(){
	this.fL = 'Page ';		
	this.hG = 'Help';
this.pO = 'Elecat';

	this.ij = 'Companies';
	this.hZ = 'http://www.elecat.com/help/EN/lhAccCompanies';
	this.ih = 'Catalogs';
	this.ik = 'http://www.elecat.com/help/EN/lhAccCatalogs';
	this.ib = 'Pages by titles';
	this.hA = 'http://www.elecat.com/help/EN/lhAccPagesTitles';
	this.ia = 'Pages by icons';
	this.ic = 'http://www.elecat.com/help/EN/lhAccPagesIcons';
	this.hB = 'Settings';
	this.hD = 'http://www.elecat.com/help/EN/lhAccSettings';
	this.ie = 'Basket';
	this.fV = 'http://www.elecat.com/help/EN/lhBasket';
	this.hY	= 'Loading data';

this.hI = 'Home';
	this.iq = 'Home page, welcome and instroduction';

	this.hu = 'Catalogs';		
	this.iy = 'Icon views and a detailed list of catalogs and pages';

	this.fG = 'Data';			
	this.fy = 'Details data that was selected in catalogs (data from the database)';  
	this.oJ = 'Flash';
this.oG = 'Enquiries';
	this.pG = 'Enter and edit enquiries and/or orders';

this.hC = 'Catalog icons';
	this.iu = 'Icons of product catalogs - use +/- buttons for zooming';

	this.hv = 'Catalog list';
	this.iw = 'Detailed list of product catalogs';

	this.qb = 'Pages';
	this.pE = 'Icons and detailed list of pages - use +/- buttons for zooming';

	this.hz = 'Catalogs gallery - icons of product catalogs';
	this.fX = 'Detailed list of product catalogs (same as in the left part)';

	this.ph = 'Pages gallery - icons of the pages in the current product catalog';
	this.oS = 'Detailed list of the pages in the current product catalog (same as in the left part)';

this.iS = 'Elecat Details window -> ';
	this.iU = 'Elecat image gallery -> ';
	this.iV = 'Elecat Yellow note window';
	this.iZ = 'Elecat Video window';
	this.iR = 'Elecat Text editor ';

this.iM = 'Tools';
	this.nc = 'http://www.elecat.com/help/EN/lhTools';

	this.jh = 'Elecat reader - e-paper product catalog';
	this.jf = 'http://www.elecat.com';
	this.ji = 'Data navigation';
	this.jk = '';
	this.jj = 'Details of the selected object in the page';
	this.pf = '';
	this.jb = '	this.contains details of the selected product or image in the page';

this.mR = 'Presents the online Elecat help';
	this.iz = 'Presents the online Elecat help';
	this.iB = 'Catalog: ';
	this.iD = 'Publisher: ';
	this.ej = 'Page No: ';
	this.eh = 'Title: ';
	this.iC = 'Image: ';

	this.tt_ynNote = 'Note';

this.iQ = 'Pages in new tab';
	this.iK = 'Open in click mode';
	this.iO = 'With help form';
	this.nJ = 'Zoom per step';
	this.iP = 'Zoom default';
	this.nD = 'Zoom';

	this.jF	= 'Beginner';
	this.jH	= 'Advanced';
	this.jJ		= 'Expert';

	this.oa = 'New page in new tab';
	this.od = '';
	this.oi = '';
	this.mZ = '';

	this.kF = 'Next page';
	this.kH = 'Click mode / move mode';
	this.nY = 'Previous page in this.ory';
	this.oh = 'Next page in this.ory';
	this.kJ = 'Actual page size';
	this.kI = 'Fit to page (show entire page)';
	this.kh = 'Fit to width (use full width)';
	this.kg = 'New pages in new tab';
	this.ki = 'Open new tab';
	this.kk = 'Seach content';
	this.kj = 'Previous page';
	this.fk = 'Zoom plus (enlarge)';
	this.mW = 'Zoom in %';
	this.fv = 'Zoom minus (shrink)';

this.eY		= 'Opens special pages of the current catalog';
	this.ob			= 'Opens views on data';				
	this.eZ		= 'Shows product lists in the details view';
	this.eU		= 'Enables you to contact the catalog publisher';
	this.fa		= 'Make enquiries / ask questions';
	this.fb			= 'Opens help pages';
	this.eT		= 'Information and links to websites related to catalogs and software';


this.kE				= 'Close';

	this.dJ				= 'Elecat release information';
	this.hX				= 'Elecat browser information';
	this.fK			= 'Elecat user information';

	this.t_buttonNoteTab1				= 'User information';
	this.t_buttonNoteTab2				= 'More Details';

this.kq = 'This page has no clickable regions';
	this.kr = 'The zoom factor is too small to draw area boxes';
	this.ke = 'You cannot close this page tab';

this.oo			= 'This feature is not yet implemented!';
	this.oq 			= 'This feature is not part of this Elecat reader version!';
	this.kb 			= 'There are no extra images on this pages!';

this.kz				= 'Pages';
	this.nG				= 'View';			
	this.ky			= 'Products';
	this.kw			= 'Contact';
	this.kv			= 'Enquiry';
	this.kx				= 'Help';
	this.kD				= 'About';
this.ka 			= 'Table of contents';
	this.jz 			= 'Index';
	this.jv 		= 'Glossary';
	this.jx 		= 'Imprint';
	this.jD 		= 'Contact data';
	this.jw 		= 'Dealers';
	this.jy 	= 'Show links on page';
	this.tl 	= 'This catalog contains no page with this content';
this.kC 		= 'Products on this page';
	this.kA 		= 'All products in this catalog';
	this.kB 			= 'Availability';
this.jB	= 'Gneral text enquiry';
	this.jo 		= 'Formal enquiry';
	this.jp 		= 'Send order';
this.jt 	= 'Request phone contact';
	this.jm 	= 'Send message via Elecat';
	this.jl 	= 'Send text enquiry';
	this.ju 		= 'Write mail';
this.jC 		= 'Help main page';
	this.jE 		= 'Help Elecat Reader';
	this.jA 	= 'Help Elecat Generator';
this.js 		= 'About this website';
	this.jq 		= 'About the publisher';
	this.jT 		= 'About this catalog';
	this.jr 		= 'About Elecat';
	this.jn 		= 'Debug mode on';

this.kL 					= 'Select language';
	this.ks = 'Select user interface language:';
	this.kt = 'User interface language was not changed';
	this.ku = 'Do you want to change the user interface language?';

	this.eI		= 'Select expert status';	
	this.kl = 'No change';
	this.km = 'Do you want to change the expert status?\nThis requires that the application is reloaded.';

this.kK 					= 'Close this tab';
	this.kM 					= 'Close other tabs';

this.eH 		= 'Articles';
	this.mP 		= 'Opens a window with a list of all products<br>on this catalog page';
	this.gx			= 'Images';
	this.np			= 'Opens a window with all images<br>on this catalog page';
	this.gG	 		= 'Prices';
	this.nm	 		= 'Opens a window with the prices of all products<br>on this catalog page';
	this.gE	 		= 'Stock';
	this.nn	 		= 'Get stock or availability data from the server<br>for all products on this catalog page';
	this.gB	 		= 'Basket';
	this.mQ	 		= 'Opens the basket for editing (add, remove, view)';
	this.cY		= 'Filled by page';

this.kn	 		= 'There are no (extra) images available from this page';
	this.kp	 		= 'Prices are available only in the Elecat server version';
	this.ko	 		= 'Stock or availability data is available\nonly in the Elecat server version';

this.cq 			= 'Insert into tab';
	this.mL	 		= 'Add the current details window to the main data tab<br>to keep it permanently';
	this.eE	 		= 'Enquiry';
	this.mK	 		= 'Open the form for entering an enquiry';
	this.eP	 		= 'Basket';
	this.mJ	 	= 'Add this article to your basket';
	this.lO 		= 'Favorites';
	this.mO 	= 'Add this article to your favorites';
	this.eO	 		= 'Stock';
	this.mI	 		= 'Get stock or availability data for this product<br>from the server';
	this.lS	 		= 'Vendors';
	this.aC	 		= 'Close form';
	this.gl	 		= 'Close this window or form';
this.eQ		= this.aC;
	this.mm_tbMenuECRDetailsCloseEditor		= this.gl;

this.bR 		= 'Send message';

this.eG 			= 'Previous';
	this.eA		 		= 'Next';
	this.lV	 		= 'Maximum';

this.hO			= 'http://www.elecat.com/help/EN/lhBasket';
	this.pV			= 'http://www.elecat.com/help/EN/lhBasket';
	this.hS		= 'http://www.elecat.com/help/EN/lhBasket';
	this.ig	= 'http://www.elecat.com/help/EN/lhBasket';

this.il	= 'Image gallery';
	this.hH			= 'Image';
	this.gJ			= 'Details';

	this.pF			= 'The selected image';
	this.it			= 'Further details of this image, if available';


this.fP			= 'Info';
	this.fR			= 'Sender';
	this.pk		= 'Receiver(s)';
	this.hE			= 'Text';
	this.pi			= 'Send';

	this.ix			= 'Some basic information about how to use this enquiry form';
	this.ip		= 'Please add your contact details in this form';
	this.qc		= 'Here you select the receiver(s) of your enquiry';
	this.im			= 'Enter your enquiry text (see info tab)';
	this.pu			= 'Send your enquiry to the selected receiver(s)';

this.oR			= 'Texts to use';
	this.hP				= 'Info';
	this.hW				= 'Basket';
	this.hQ		= 'Text modules';
	this.hR		= 'Questions';

this.hw 	= 'Details';
	this.pg 	= 'An html page with additional product details';
	this.hy 	= 'Details2';
	this.oD 	= 'Another html page (optional) with more additional product<br>details generated from the database';
	this.hx 	= 'Details3';
	this.oz 	= 'A third html page (optional) with more additional product<br>details generated from the database';
	this.hJ	= 'Pictures';
	this.pn 	= 'More pictures of the selectec product in an html page';
	this.hM = 'Technical';
	this.qd = 'This tab can hold an html page with technical information<br>and this page can also be generated from the database';
	this.hN	= 'Related products';
	this.pW	= 'Shows an html page with products that are<br>somehow related to this current product like<br>accessories, spare parts, alternative products etc';
	this.hL	= 'Pricing';
	this.pM	= 'Gets pricing data for the current product from the server';
	this.hK		= 'Order';
	this.oB		= 'Opens the order form where you can enter data<br>to make an enquiry or place an order';
	this.hF	= 'Yellow note';
	
	this.or 		= 'About Elecat';

	this.n_infoFinalRelease = 'This will work in the final release.';
	this.n_infoNextRelease = 'This will be part of the next Elecat release.';
	this.n_infoNextReleaseServer = 'This will be part of the Elecat server version,<br>which will be the next release.';


this.kd						= 'The Elecat application is being initialized';
	this.kf					= 'Initializing the Elecat application';
	this.nd							= 'There is some problem in: ';
	this.ft		= 'Please enter some keyword(s)!';
	this.fA		= 'No result available';
	this.hT		= 'A mximum of XX new tabs were created';
	this.gL		= 'Loading.....please wait.....';
	this.hU			= 'No page selected';

this.hV				= 'Because this is just a demo version, nothing was really sent!';

this.jS					= 'Elecat search window';

this.je				= 'Search';
	this.iE				= 'New search: Search in html and catalog pages';
	this.jc				= 'Advanced search';
	this.iF			= 'The advanced search offers more selection criteria';

	this.jd				= 'Database search';
	this.iG			= 'Search the server database (please read Help for more explanations)';

	this.pd			= 'Last result';
	this.nq			= 'Contains the result of the previous search';

	this.t_searchFormTopTabResultNrXXX			= 'Result XXX';
	this.iJ			= 'Contains an earlier result; you can close the tab via the (x) button';


	this.ja				= 'Search for';
	this.ir				= 'IS this shown ';

	this.sA				= 'Search result';
this.jU		= 'No result yet available';  
	this.l_searchResultPanelHtmlNothingFound 	= 'Nothing found! Please enter some other search terms.';	
	this.t_searchResultPreviewXXX				= 'Preview XXX';
	this.iI					= 'Result preview BOTTOM TIP';

	this.iv			= 'Page preview XXX';
	this.iA		= 'Press "Page to tab" in toolbar to open<br>this page in a larger "data" tab';

this.cS					= 'Current search result';
	this.t_searchBasicTabResultXXX				= 'Search result #XXX';
	this.fn				= 'This is the result of the last search; to remember, press the button "Store results".';
	this.cS					= 'Current search result';

	this.eu			= 'Result previews';
	this.fz		= 'This is useful for a quick selection of search results, which can later be viewed in a larger data tab. Press "Result to tab".';

	this.ou			= 'New search';
	this.na			= 'Clear the search field and the last search result';
	this.ov		= 'Perform search';
	this.mX		= 'Perform a search for the entered keywords';
	this.ot			= 'Store results';
	this.mY		= 'Remember last search result, move to a new tab';

	this.jI				= 'Search in';
	this.jW				= 'Entire site';
	this.jV				= 'Only catalog pages';
	this.jX				= 'Only product data pages';

this.jP				= 'Type of search';
	this.jY					= 'Any';
	this.jZ					= 'All';
	this.jR				= 'Exact';


this.gq 			= 'Search result to tab';
	this.nz 			= 'Adds the current result summary page to a new result tab<br>(here in the search window) to remember the search results';
	this.gn 		= 'All results to tab    - '; 
	this.nu 		= 'Adds up to 10 summary pages to new result tabs<br>(here in the search window) to make these pages visible';

	this.cP 			= 'Preview page';
	this.nt 			= 'Adds current page from preview to a data tab<br>(in the main tabs "Data") with more space.<br>Checkbox "Result preview" must be enabled<br>and a preview page selected.';
	this.gC 			= 'Preview all pages  ';
	this.ny			= 'Adds all page referenced on the current result summary preview to new data tabs<br>(in the main tabs "Data") with more space';

	this.dW 		= 'Result preview';
	this.kN 	= 'Result preview is now used! This means:\nA click on a search result page link opens this page\nin the preview tab here in the search window';
	this.kO	= 'Result preview is NOT used! This means:\nA click on a search result page link opens this page\nin a new tab inside the main "data" tab';
	};
	}

	tr.dO();
	t.dO();

