
/**
 *  Object
 */
Object.prototype.extend=function(object){
        var target=(arguments.length>1)?arguments[1]:this;
        try{
            for(var property in object){
                target[property]=object[property];
            }
            return target;
        }catch(e){
            return false;
        }
}
Object.prototype.extend({
    toBoolean:function(object){
        return object?true:false;
    },
    toArray:function(object){
        var a=[];
            object=object || this;
        for(var c=0;c<object.length;c++){
            a[a.length]=object[c];
        }
        return a;
    },
    get$properties:function(object){
        if (!arguments.length){ object=this; }
        var all=[];
        Object.each$properties(
            function(all,property){
                all.push(property);
            }.toMethod(this,all)
            ,object
        );
        return all;
    },
    each$properties:function(callback,object){
        if (!Object.isFunction(callback)){ return; }
        if (arguments.length<2){ object=this; }
        try{
            for(property in object){
                try{ callback.call(this,property,object[property]); } catch(e){}
            }
        } catch(e){}
        return object;
    },
    new$properties:function(object){
        if (!arguments.length){ object=this; }
        var found=[];
        Object.each$properties(
            function(found,property,value){
                if (Object.isUndefined(Object[property]) ||
                    (Object[property]!==value)
                ){
                    found.push(property);
                }
            }.toMethod(this,found)
            ,object
        );
        return found;
    },
    include:function(object,target){
        if (Object.isUndefined(target) || Object.isUndefined(object)){ return; }
        try{
            Object.new$properties(object).foreach(
                function(target,object,property){
                    try{ target[property]=object[property]; } catch(e) {}
                }.toMethod(this,target,object)
            );
        } catch(e){}
        return target;
    },
    serialize:function(object){
        if (!arguments.length){ object=this; }
        var all=[];
        Object.each$properties(
            function(all,property,value){
                var type=typeof value,
                    sValue='';
                switch(type){
                    case 'string':
                            sValue='"'+value.truncate(30)+'"';
                            if (value.length>30){
                                sValue+=' (...)';
                            }
                        break;
                    case 'function':
                            sValue=value.toString()
                                .replace(/[\r\n]/g,'')
                                .replace(/^function[\s\t\r\n\v]*\(/,'')
                                .match(/^[^\(\)]+/);
                            sValue='('+(sValue || '')+')';

                        break;
                    case 'undefined':
                        sValue='undefined';
                        break;
                    default:
                        sValue=value;
                        break;
                }
                all.push(type+':'+property+' = '+sValue+'');
            }.toMethod(object,all)
            ,object
        );
        return all.join("\n");
    },
    toHash:function(object){
        object=Object.isUndefined(object)?this:object;
        if (object._typeHash){ return object; }
        var pairs=[];
        Object.each$properties(
            function(pairs,property,value){
                if (Object.isUndefined(Object[property]) ||
                    (Object[property]!==value)
                ){
                    pairs.push({
                        key:property,
                        value:value
                    });
                }
            }.toMethod(this,pairs)
            ,object
        );
        return pairs.extend({
            _typeHash:true,
            getKey:function(value){
                var struct=this.search(value);
                return struct && struct.key;
            },
            getKeys:function(){
                return this.fetch('key');
            },
            getPair:function(name){
                return Object.isString(name) &&
                    this.foreach(
                        function(name,pair,c){
                            if (name==pair.key){
                                return pair;
                            }
                        }.toMethod(this,name)
                    );
            },
            getIndex:function(name){
                return Object.isString(name) &&
                    this.foreach(
                        function(name,pair,c){
                            if (name==pair.key){
                                return c;
                            }
                        }.toMethod(this,name)
                    );
            },
            get:function(name){
                var pair=this.getPair(name);
                return pair && pair.value;
            },
            set:function(name,value){
                if (!Object.isString(name)) { return; }
                var pair=this.getPair(name);
                if (!pair){
                    this.push(
                        pair={
                            key:name,
                            value:null
                        }
                    );
                }
                pair.value=value;
                return this;
            },
            add:function(){
                return this.set.apply(this,Object.toArray(arguments));
            },
            isSet:function(name){
                return this.foreach(
                        function(name,pair){
                            if (pair.key==name){
                                return true;
                            }
                        }.toMethod(this,name)
                    ) || false;
            },
            remove:function(){
                var args=Object.toArray(arguments);
                args.foreach(
                    function(value){
                        var pairIndex=this.getIndex(value);
                        if (Object.isNumber(pairIndex)){
                            this.splice(pairIndex,1);
                        }
                    }.toMethod(this)
                );
                return this;
            },
            serialize:function(format){
                var items=[],
                    into=Object.toArray(arguments);
                if (arguments.length){ into.shift(); }
                if (!format || !Object.isString(format)){ format='%name% = %type%: "%value%"'; }
                this.foreach(
                    function(items,format,into,pair){
                        var value=Object.fetchValue(pair.value,into);
                        try{
                            items.push(
                                format.replace(/%name%/,pair.key)
                                    .replace(/%type%/,typeof pair.value)
                                    .replace(/%value%/,value)
                            );
                        } catch(e){ items.push(pair.key+'=(cannot be defined)') }
                    }.toMethod(this,items,format,into)
                );
                return items.join("\n");
            },
            //-- override search
            search:function(value){
                if (!Object.isUndefined(value)){
                    return this.foreach(
                        function(searchValue,pair,c){
                            if (pair.value===searchValue){
                                return pair;
                            }
                        }.toMethod(this,value)
                    );
                }
            },
            toObject:function(){
                var object={};
                this.foreach(
                    function(object,pair){
                        object[pair.key]=pair.value;
                    }.toMethod(this,object)
                );
                return object;
            }
        });
    }
});

Object.extend(
    {
        isElement: function(object) {
            return object && object.nodeType == 1;
        },
        isTextNode: function(object) {
            return object && object.nodeType == 3;
        },
        isArray: function(object) {
            return object && object.constructor === Array;
        },
        isFunction: function(object) {
            return typeof object == "function";
        },
        isString: function(object) {
            return typeof object == "string";
        },
        isNumber: function(object) {
            return typeof object == "number";
        },
        isUndefined: function(object) {
            return typeof object == "undefined";
        },
        isScalar:function(object){
            return Object.isNumber(object) ||
                Object.isString(object);
        },
        fetchValue:function(object,into){
            if (!object || !Object.isArray(into)){ return; }
            var args=[].concat(into),
                property=null;
            while(object && (property=args.shift()) && Object.isString(property)){
                object=object[property];
            }
            return object;
        },
/**
 *  IPC
 */
        
/**
 *  ERROR REPORTING
 */
        reportError:function(e){
            if (Object.isUndefined(Object._errors)){
                Object.__errors__=[];
            }
            var error=(e instanceof Error)?e.getDetails():(e || 'unknown');
            
            Object.__errors__[Object.__errors__.length]=error;
            //-- use error handler
            if (Object.isFunction(Object.onerror)){
                Object.onerror(error);
                
            //-- throw exception
            } else {
                //self.openErrorConsole();
                alert(error);

            }
        }
    }
);

/**
 *  Error handle
 */

Error.prototype.extend(
    {
        getDetails:function(){
            var message='';
            if (this.description){
                message+=' description: '+this.description+"\n";
                    
            } else if(this.message){
                message+=' description: '+this.message+"\n";
            }
            
            if (this.stack && Object.isString(this.stack)){
                message+=' trace: '+
                    (this.message)+
                    this.stack.replace(/\)@/g,")@\r\n")+"\n";
            }
            if (!message && Object.isFunction(this.toString)){
                message=this.toString()+"\n";
            }
            return message;
        }
    }
);






/**
 * Error Console
 */
self.openErrorConsole=function(){
    var errorMessages=Object.__errors__ && Object.__errors__.join("\n ----- ERROR ----- \n"),
        newConsole=!self.__error_console__,
        console=self.__error_console__ || self.open(Object.modulePath()+'_blank.html',
            'ErrorConsole',
            'resizable,scrollbars,status,height=200,width=500'
        );

    console.document.open('text/plain');
    console.document.write(errorMessages);
    console.document.close();
    if (newConsole){
        self.__error_console__=console;
        console.onload=function(){
            this.__error_console__.onunload=function(){
                this.__error_console__=null;
            }.toMethod(this);

        }.toMethod(self);
    }
}

/**
 * String
 */
String.prototype.extend(
    {
        urlencode:function(value){
            return escape(arguments.length?value:this);
        },
        urldecode:function(value){
            return unescape(arguments.length?value:this);
        },
        htmlencode:function(){
            var div=document.createElement('div'),
                text=document.createTextNode(this);
            div.appendChild(text);
            return div.innerHTML;
    	},
        toChildNodes:function(){
            var div=document.createElement('div');
            div.innerHTML=this;
            return Object.toArray(div.childNodes);
        },
        ucwords:function(){
            var value=new String();
            if (this.length){
                for(var c=0;c<this.length;c++){
                    var character=this.charAt(c);
                    if ((c==0) ||
                        (c && this.charAt(c-1).match(/[^a-z\-]/i))
                    ){
                        value+=character.toUpperCase();
                    } else {
                        value+=character;
                    }
                }
            }
            return value;
        },
        padLeft:function(padding,limit){
            var str=this.toString();
            if (Object.isString(padding) && Object.isNumber(limit)){
                if (str.length<limit){
                    for(var c=str.length;c<limit;c++){
                        str=padding+str;
                    }
                }
            }
            return str;
        },
        repeat:function(limit){
            if (!Object.isNumber(limit) || (limit<1)){
                return '';
            }
            var str='';
            for(var c=1;c<=limit;c++){
                str+=this.toString();
            }
            return str;
        },
        trimLeft:function(){
            return this.replace(/^[\s\t\r\n\v]+/,'');
        },
        trimRight:function(){
            return this.replace(/[\s\t\r\n\v]+$/,'')
        },
        trim:function(){
            return this.trimLeft().trimRight();
        },
        truncate:function(limit,append){
            if (!Object.isNumber(limit)){ return this; }
            var str=this.valueOf(),
                append=Object.isString(append)?append:'';
            if ((limit>0) && (str.length>limit)){
                str=this.slice(0,limit)+append;
            }
            return str;
        }
    }
);

/**
 *  Number
 */
Number.$hex="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Number.prototype.extend(
    {
        baseConvert:function(base,number){
            if (!Object.isNumber(base) || (base<=0) || (base>=Number.$hex.length)){ return; }
            var value=Object.isNumber(number)?number:this,
                newvalue='';
            while(value>=base){
                var r=value%base;
    			value=Math.floor(value/base);
                newvalue=Number.$hex.charAt(r)+newvalue;
            }
            if (value){
                newvalue=Number.$hex.charAt(value)+newvalue;
            }
            if (base==10){
                return parseInt(newvalue);
            } else {
                return newvalue;
            }
        },
        hex:function(number){
            return this.baseConvert(16,Object.isUndefined(number)?this:number);
        },
        octal:function(number){
            return this.baseConvert(8,Object.isUndefined(number)?this:number);
        },
        binary:function(number){
            return this.baseConvert(2,Object.isUndefined(number)?this:number);
        }
    }
);


/**
 *  Date
 */
Date.extend({
    $wk:['sun','mon','tues','wed','thur','fri','sat'],
    $week:['sunday','monday','tuesday','wednesday','thursday','friday','saturday'],
    $mnth:['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'],
    $month:['january','february','march','april','may','june','july','august','september','october','november','december']
});
Date.prototype.extend(
    {
        getMaxDays:function(){
    		var month=this.getMonth(),
	    		year=this.getFullYear(),
		    	maxMonths=[31,(year%4)?29:28,31,30,31,30,31,31,30,31,30,31];
    		return maxMonths[month];
	    },
        getTimestamp:function(){
            return Math.round(this.getTime()/1000.0);
        },
        setTimestamp:function(time){
            var time=parseInt(time);
            if (Object.isNumber(time)){
                this.setTime(time*1000.0);
            }
        },
        format:function(format,timestamp){
    		if (!Object.isString(format)){
	    		return;
            }
            if (Object.isNumber(timestamp) &&
                (timestamp>=0)
            ){
	    		this.setTime(timestamp);
            }
            var time={
                'a':(this.getHours()<12)?'am':'pm',
                'A':(this.getHours()<12)?'AM':'PM',
                
                'd':this.getDate(),
                'D':Date.$wk[this.getDay()]||'',
                'F':Date.$month[this.getMonth()],
                
                'h':((this.getHours()%12)+1),
                'H':this.getHours(),
                'i':this.getMinutes(),
                
                'l':Date.$week[this.getDay()]||'',
                'L':!(this.getFullYear()%4),
                'm':(this.getMonth()+1),
                'M':Date.$mnth[this.getMonth()],
                
                'O':this.getTimezoneOffset(),
                'N':this.getDay()?(this.getDay+1):'',
                's':this.getSeconds(),
                'u':this.getTime(),
                'w':this.getDay(),
                'y':this.getYear(),
                'Y':this.getFullYear()
            }.toHash();
            
            var newFormat='';
            for(var c=0;c<format.length;c++){
                var chr=format.charAt(c),
                    value=time.get(chr);
                newFormat+=!Object.isUndefined(value) ? value : chr; 
            }
            return newFormat;
        }
    }
);
/**
 *  Function
 */
Function.prototype.extend(
    {
        toMethod:function(){
            var __function__=this,
                args=Object.toArray(arguments),
                context=args.length&&args.shift() || __function__;
            return context &&
                function(){
                    return __function__.apply(context,args.concat(Object.toArray(arguments)));
                }
        },
        toEventMethod:function(){
            var __function__=this,
                args=Object.toArray(arguments),
                context=args.length&&args.shift() || __function__;
            return context &&
                function(e){
                    return __function__.apply(context,[e||self.event].concat(args));
                }
        },
        toEventMethodByWindow:function(){
            var __function__=this,
                args=Object.toArray(arguments),
                context=args.length&&args.shift() || __function__,
                win=args.length&&args.shift() || self;
            return context &&
                function(e){
                    return __function__.apply(context,[e||win.event].concat(args));
                }
        },
        callEvery:function(interval){
            if (Object.isNumber(interval) &&
                (interval>=0)
            ){
                var ticket={
                        onload:null,
                        cancel:cancel=function(){
                            try{ self.clearInterval(this.id); } catch(e){}
                        },
                        reference:this
                    };
                ticket.id=self.setInterval(
                    function(){
                        if (Object.isFunction(this.onload)){ this.onload(); }
                        this.reference.call(this);
                    }.toMethod(ticket)
                    ,interval
                );
                //-- create timeout ID
                return ticket;
            }
        },
        callOnce:function(interval){
            var ticket=this.callEvery(interval);
            if (ticket){
                ticket.onload=function(){
                    this.cancel();
                };
            }
            return ticket;
        }
    }
);




/**
 *  Array
 */
Array.prototype.extend(
    {
        search:function(value){
            if (!Object.isUndefined(value)){
                return this.foreach(
                    function(searchValue,value,c){
                        if (value==searchValue){
                            return {
                                key:c,
                                value:value
                            };
                        }
                    }.toMethod(this,value)
                );
            }
        },
        foreach:function(func,returns){
            if (Object.isFunction(func)){
                var clone=[].concat(this);
                for(var c=0;c<clone.length;c++){
                    var returned=func.call(this,clone[c],c,this);
                    if (!Object.isUndefined(returned)){
                        return returned;
                    }
                }
                if (returns){ return this; }
            }
        },
        each:function(func){
            this.foreach(func);
            return this;
        },
        fetch:function(){
            var args=Object.toArray(arguments),
                newArray=[];
            this.foreach(
                function(properties,value){
                    var args=[].concat(properties),
                        object=null,
                        property=null;
                    while(value && (property=args.shift()) && Object.isString(property)){
                        object=value[property];
                    }
                    this[this.length]=object;
                }.toMethod(newArray,args)
            );
            return newArray;
        },
        remove:function(){
            var args=Object.toArray(arguments),
                counter=this.length,
                copy=[].concat(this),
                last=0;
            for(var c=0;c<args.length;c++){
                if (Object.isNumber(args[c]) && !Object.isUndefined(copy[args[c]])){
                    delete copy[args[c]];
                    counter--;
                }
            }
            //-- renumber
            for(var c=0;c<this.length;c++){
                if (!Object.isUndefined(copy[c])){
                    this[last]=copy[c];
                    last++;
                }
                if (counter<c){
                    delete this[c];
                }
            }
            this.length=counter;
            return this;
        },
        removeAll:function(){
            while(this.length){
                this.shift();
            }
        }
    }
);

/**
 *  Application
 */

/**
 *  Class
 */
Object.prototype.extend(
    {
        isClass:function(object){
            return Object.isFunction(object) &&
                Object.isString(object.__class__) &&
                Object.isString(object.__class_path__);
                
        },
        isClassObject:function(object){
            return object && Object.isString(object.$className);
        }
    }
);

Class={
    __class__:'Class',
    __class_path__:'Class',
    __class_base__:null,
    __class_requires__:null,
    create:function(name,baseClass){
        if (!name || !Object.isString(name)){
            Object.reportError('invalid class name: '+name);
            return;
        }
        //-- create constructor
        this[name]=function(){
            this.initialize.apply(this,Object.toArray(arguments));
        };
        //-- build static class methods 
        Class.new$properties().foreach(
            function(def,property){
                //-- do not include classes
                if (!Object.isClass(def[property])){
                    this[property]=def[property];
                }
            }.toMethod(this[name],Class)
        );
        //-- rebuild base class methods
        this[name].prototype={};
        
        //-- create initial class members from base class
        if (Object.isFunction(baseClass)){
            this[name].declare(baseClass.prototype);
            this[name].__class_base__=baseClass;
        } else {
            this[name].__class_base__=null;
        }
        
        //-- recreate class name
        this[name].__class__ = name;
        this[name].__class_path__ = this.__class_path__+'.'+name;
        this[name].__class_requires__ = [];
        this[name].__class_requires_package__ = [];
        
        //-- declare initial methods
        this[name].declare({
            $declaration:this[name],
            $class:this[name].__class__,
            $className:this[name].__class_path__,
            $base:this[name].__class_base__,
            $baseMethod:function(){
                    var args=Object.toArray(arguments),
                        name=args.length && args.shift(),
                        baseFunc=Object.isClass(this.$base) &&
                            Object.isString(name) &&
                            this.$base.prototype[name];
                    
                    return Object.isFunction(baseFunc) &&
                        baseFunc.apply(this,args);
            },
            initialize:function(){},
            //-- should only extend declared object
            extend:Class.declare,
            //-- object messenging
            ipc$application:null,
            ipc$onmessage:null,
            $alert:function(name,value){
                if (Object.isString(name) &&
                    Object.isFunction(this.onchange)
                ){
                    this.onchange(name,value);
                }
                if (this.ipc$application &&
                    Object.isFunction(this.ipc$application.onmessage)
                ){
                    this.ipc$application.onmessage(this,name,value);
                }
            }
        });
        return this[name];
    },
    declare:function(object){
        if (this!=Class){
            Object.include(object,this.prototype);
        }
        return this;
    },
    getSubClasses:function(className){
        if (!Object.isString(className=this.getClassName(className))){ return; }
        var parentClasses=className.replace(/^Class\./,'').split('.'),
            last='Class';
        return parentClasses.foreach(
            function(className,c){
                this.value+='.'+className;
                parentClasses[c]=this.value;
            }.toMethod({value:last})
            ,true
        );
    },
    getClassName:function(className){
        return className &&
            Object.isString(className) &&
            className.match(/^[a-z_]+[a-z0-9_]*(\.[a-z_]+[a-z0-9_]*)*$/i) &&
            'Class.'+className.replace(/^class\./i,'');
    },
    exists:function(className){
        if (!Object.isString(className=this.getClassName(className))){ return false; }
        var subClasses=className.replace(/^Class\./,'').split('.'),
            last=Class;
        while(subClasses.length && (last=last[subClasses.shift()]));
        return last;
    }
    
}
//-- create Dmc package
Class.create('Dmc');

/**
 *  Main Dmc Methods
 */
self.$dmc={
        viewIndex:{
            shroud:10000,
            message:5000,
            popupmenu:4000,
            modal:3000,
            dialog:3000,
            windows:2000
        }.toHash(),
        modulePath:function(){
            var url=self.location.href,
    		    domain=url.replace(/^(http\:\/\/[^\/]+).*$/,"$1"),
                head=document.getElementsByTagName &&
                    document.getElementsByTagName('head')[0],
                scripts=head &&
                    head.getElementsByTagName('script');
                
            return scripts &&
                Object.toArray(scripts).foreach(
                    function(script){
                        var src=Object.isElement(script) &&
                                script.getAttribute('src');
                        if (Object.isString(src)&&src.match(/base\.js$/)){
                            return src.replace(/[^\/]+$/,'');
                        }
                    }
                );
        },
        rootPath:function(){
            var mod=this.modulePath();
            return mod && mod.replace(/[^\/]+\/$/,'');
        },
        loadModule:function(name){
            if (name && Object.isString(name)){
                var fileName=this.modulePath()+name+'.js';
                if (!self.loaded){
        			document.write('<script language="javascript" type="text/javascript" src="'+fileName+'"></script>');
                } else {
                    Object.reportError('script to load: '+fileName+' must be loaded before window loads');
                }
            }
        }
    };
/**
 *  window Events
 */
self.cancelEvent=function(e){
    if (e.type && Object.isString(e.type)){
        e.cancelBubble=true;
        //-- cancel event
        try{
            e.preventDefault();
        } catch(e){
            e.returnValue=false;
        }
        return false;
    }
}

self.addDocumentEvent=function(doc,name,callback){
    if (!name || !Object.isString(name) || !Object.isFunction(callback)){ return; }
    if (self.addEventListener){
        try{
            doc.addEventListener(name,callback,false);
        } catch(e){ return; }
    } else if (self.attachEvent) {
        try {
            doc.attachEvent('on'+name,callback);
        } catch(e){ return; }
    }
};
self.addWindowEvent=function(win,name,callback){
    if (!name || !Object.isString(name) || !Object.isFunction(callback)){ return; }
    if (self.addEventListener){
        try{
            win.addEventListener(name,callback,false);
        } catch(e){ return; }
    } else if (self.attachEvent) {
        try {
            win.attachEvent('on'+name,callback);
        } catch(e){ return; }
    }
};
/**
 *  Flag window is loaded on load
 */
self.loaded=false;

addWindowEvent(self,'load',
    function(){ self.loaded=true; }
);
addWindowEvent(self,'unload',
    function(e){
        if (self.__error_console__){ self.__error_console__.close(); }
    }
);



/**
 *  Load Modules
 */

$dmc.loadModule('Xml');
$dmc.loadModule('Dom');
$dmc.loadModule('Iframe');
$dmc.loadModule('Http');
$dmc.loadModule('Form');
$dmc.loadModule('Application');
/**/

