/*
Extensible Javascript(ejs)
@author lionelpang
@version 1.0b
*/

var ejs;
(function(){
    String.prototype.trim = function(){
        return this.replace(/(^\s+)|(\s+$)/g, '');
    };
    var _clone = function(object){
        var _var;
        if(object.constructor == Object)
            _var = new object.constructor();
        else
            _var = new object.constructor(object.valueOf());
        for(var key in object){
            if(_var[key]!=object[key]){
                if(typeof(object[key])=='object'){
                    _var[key] = object[key].clone();
                }
                else{
                    _var[key] = object[key];
                }
            }
        }
        _var.toString = object.toString;
        _var.valueOf = object.valueOf;
        return _var;
    };
    var _userAgent = navigator.userAgent.toLowerCase();
    if(!ejs){
        ejs = {
            $ : function(){
                return document.getElementById(arguments[0]);
            },
            isObject : function(object){
                return typeof(object)=='object';
            },
            isArray : function(object){
                return object && object.constructor==Array;
            },
            isString : function(object){
                return typeof(object)=='string';
            },
            isFunction : function(object){
                return typeof(object)=='function';
            },
            isDate : function(object){
                return object instanceof Date;
            },
            isNumeric : function(object){
                return /^\d+$/.test(object);
            },
            isRegexp : function(object){
                return object instanceof RegExp;
            },
            isClass : function(object){
                return object && object.hasOwnProperty('__CLASS_TYPE__') && object.__CLASS_TYPE__=='EJS.CLASS';
            },
            isInterface : function(object){
                return object && object.hasOwnProperty('__CLASS_TYPE__') && object.__CLASS_TYPE__=='EJS.INTERFACE';
            },
            isInstance : function(object){
                return object && ejs.isClass(object.constructor);
            },
            browser:{
                version: (_userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
                safari: /webkit/.test( _userAgent ),
                opera: /opera/.test( _userAgent ),
                msie: /msie/.test( _userAgent ) && !/opera/.test( _userAgent ),
                mozilla: /mozilla/.test( _userAgent ) && !/(compatible|webkit)/.test( _userAgent )
            },
            isStandardMode : (document.compatMode=='CSS1Compat'),
            clone : _clone,
            cancelBubble : function(){
                var evt = arguments[0] || window.event;
                if(evt){
                    if(evt.stopPropagation)
                        evt.stopPropagation();
                    else	
                        evt.cancelBubble = true;
                }
            }
        };
 
//event handle    
		ejs.addEventListener = document.addEventListener?function(/*dom-element*/elem, /*string*/eventName, /*function*/listener, /*boolean*/capture){
            return elem.addEventListener(eventName, listener, capture||false);
        }:
        (document.attachEvent?function(/*dom-element*/elem, /*string*/eventName, /*function*/listener){
            return elem.attachEvent('on'+eventName, listener);
        }:function(){ return null; });
		ejs.removeEventListener = document.removeEventListener?function(/*dom-element*/elem, /*string*/eventName, /*function*/listener, /*boolean*/capture){
            return elem.removeEventListener(eventName, listener, capture||false);
        }:
        (document.detachEvent?function(/*dom-element*/elem, /*string*/eventName, /*function*/listener){
            return elem.detachEvent('on'+eventName, listener);
        }:function(){ return null; });
		ejs.dispatchEvent = document.dispatchEvent?function(/*dom-element*/elem, /*string*/eventName){
            var _event = document.createEvent("Events");
			_event.initEvent(eventName, true, false);
			return elem.dispatchEvent(_event); 
        }:
        (document.fireEvent?function(/*dom-element*/elem, /*string*/eventName){
            var _event = document.createEventObject();
            return elem.fireEvent('on'+eventName,_event);
        }:function(){ return null; });

/************* core begin *****************/
		var __public_declare__ = function(){ this.properties = {}; };
		var __private_declare__ = function(){ this.properties = {}; };
		var __protected_declare__ = function(){ this.properties = {}; };
		var __static_declare__ = function(){ this.properties = {}; };
		var __interface_properties__ = function(){ this.properties = {}; };

		var __define__ = function(){
			var _class = arguments[0];
			return function (){
				for(var i=0;i<arguments.length;++i){
					if(arguments[i] instanceof __public_declare__){
						__copy_properties__(_class, arguments[i]);
					}
					else if(arguments[i] instanceof __private_declare__){
						__copy_properties__(_class, arguments[i]);
					}
					else if(arguments[i] instanceof __protected_declare__){
						__copy_properties__(_class, arguments[i]);
					}
					else if(arguments[i] instanceof __static_declare__){
						__copy_static_properties__(_class, arguments[i]);
					}
				}
				_class.__DECLARED__ = true;
				return _class;
			};
		};
		var __interface_define__ = function(){
			var _class = arguments[0];
			return function (){
				for(var i=0;i<arguments.length;++i){
					if(arguments[i] instanceof __public_declare__){
						__copy_interface_properties__(_class, arguments[i]);
					}
					else if(arguments[i] instanceof __protected_declare__){
						__copy_interface_properties__(_class, arguments[i]);
					}
				}
				_class.__DECLARED__ = true;
				return _class;
			};
		};
		var __implements__ = function(){
			var _class = arguments[0];
			return function(){
				if(!(_class.__DECLARED__ || _class.__IMPLEMENTED__)){
					for(var i=0;i<arguments.length;++i){
						if(ejs.isInterface(arguments[i]) && arguments[i].__DEFINE__ instanceof __interface_properties__){
							for(var p in arguments[i].__DEFINE__.properties){
								if(p=='construct')
									_class.__INITIALIZE__.push(arguments[i].__DEFINE__.properties[p]);
								else
									_class.prototype[p] = arguments[i].__DEFINE__.properties[p];
							}
						}
					}
					_class.__IMPLEMENTED__ = true;
				}
				return _class;
			};
		};
		var __extends__ = function(){
			var _class = arguments[0];
			return function(){
				var _parentClass = arguments[0];
				if((ejs.isClass(_parentClass) || ejs.isInterface(_parentClass)) && !(_class.__DECLARED__ || _class.__EXTENDED__ || _class.__IMPLEMENTED__)){
					_class.prototype = new _parentClass();
					_class.prototype.$super = _parentClass.prototype;
					_class.prototype.$parent = _parentClass;
					_class.prototype.constructor = _parentClass;
					__delete_private_properties__(_class);
					
					_class.__EXTENDED__ = true;
					_class.__IMPLEMENTED__ = _class.__DECLARED__ = false;
				}
				return _class;
			};
		};

		var __copy_properties__ = function(_class, declare){
			for(var p in declare.properties){
				_class.prototype[p] = declare.properties[p];
			}
		};
		var __copy_interface_properties__ = function(_class, declare){
			if(!(_class.__DEFINE__ instanceof __interface_properties__))
				_class.__DEFINE__ = new __interface_properties__();
			for(var p in declare.properties){
				_class.__DEFINE__.properties[p] = declare.properties[p];
			}
		};
		var __copy_static_properties__ = function(_class, declare){
			for(var p in declare.properties){
				_class[p] = declare.properties[p];
			}
		};

		var __delete_private_properties__ = function(_class){
			for(var p in _class.prototype){
				if(/^_\$/.test(p)){
					_class.prototype[p] = undefined;
				}
			}
			return _class;
		};
		//class declare
		var __class__ = function(){
			if(!arguments[0])
				throw new Error('Class\'s name is undefined.');
			var _var = function(){
				if(ejs.isArray(this.constructor.__INITIALIZE__)){
					for(var i=0;i<this.constructor.__INITIALIZE__.length;++i){
						this.constructor.__INITIALIZE__[i].apply(this, arguments);
					}
				}
				ejs.isFunction(this.construct) && this.construct.apply(this, arguments);
			};
			_var.__NAME__ = arguments[0];
			_var.__CLASS_TYPE__ = 'EJS.CLASS';
			_var.$extends = __extends__(_var);
			_var.$implements = __implements__(_var);
			_var.$define = __define__(_var);
			_var.toString = function(){ return ['class ', _var.__NAME__].join(''); };
			_var.__INITIALIZE__ = [];
			return _var;
		};
		//interface declare    
		var __interface__ = function(){
			if(!arguments[0])
				throw new Error('Class\'s name is undefined.');
			var _var = function(){
				throw new Error('Interface class can not be instanced.');
			};
			_var.__NAME__ = arguments[0];
			_var.__CLASS_TYPE__ = 'EJS.INTERFACE';
			_var.$extends = __extends__(_var);
			_var.$define = __interface_define__(_var);
			_var.toString = function(){ return ['interface ', _var.__NAME__].join(); };
			return _var;
		};
		//import package method
		var __import_package__ = function(){
			var _namespace = arguments[0];
			if(ejs.isString(_namespace)){
				var _a_namespace = _namespace.split('.');
				if(_a_namespace[_a_namespace.length-1]=='*'){
					_a_namespace.splice(_a_namespace.length-1, 1);
					if(_a_namespace.length>0){
						try{
							eval([
								'var _package = ', _a_namespace.join('.'),';'
							].join(''));
							if(ejs.isObject(_package)){
								for(var _class in _package){
									if(ejs.isClass(_package[_class]) || ejs.isInterface(_package[_class])){
										window[_package[_class].__NAME__] = _package[_class];
									}
								}
							}
						}
						catch(e){ }
					}
				}
				else{
					try{
						eval([
							'var _class = ', _namespace,';'        
						].join(''));
						if(ejs.isClass(_class) || ejs.isInterface(_class)){
							window[_class.__NAME__] = _class;
						}
					}
					catch(e){ }
				}
			}
		};
		//create package method
		var __package__ = function(){
			var _start, _a_name;
			if(ejs.isString(arguments[0]) && arguments[0]){
				_start = 1;
				_a_name = arguments[0].split('.');
			}
			else{
				_start = 0;
				_a_name = [];
			}

			var _namespace = [];
			for(var i=0;i<_a_name.length;++i){
				if(_a_name[i].trim()=='*')
					return;
				_namespace.push(['["', _a_name[i].trim(), '"]'].join(''));
				eval([
					'if(!window', _namespace.join(''),'){',
                    'window', _namespace.join(''), ' = {};',
					'}'
				].join(''));
			}
			for(var i=_start;i<arguments.length;++i){
				if(ejs.isClass(arguments[i]) || ejs.isInterface(arguments[i])){
					eval([
						'window', _namespace.join(''), '[arguments[i].__NAME__] = arguments[i];'       
					].join(''));
				}
			}
		};
		//keyword
		$public = function(){
			var _declare = ejs.isObject(arguments[0])?arguments[0]:{};
			var _sagment = new __public_declare__();
			for(var p in _declare){
				_sagment.properties[p] = _declare[p];
			}
			return _sagment;
		};
		$private = function(){
			var _declare = ejs.isObject(arguments[0])?arguments[0]:{};
			var _sagment = new __private_declare__();
			for(var p in _declare){
				_sagment.properties['_$'+p] = _declare[p];
			}
			return _sagment;
		};
		$protected = function(){
			var _declare = ejs.isObject(arguments[0])?arguments[0]:{};
			var _sagment = new __protected_declare__();
			for(var p in _declare){
				_sagment.properties['_'+p] = _declare[p];
			}
			return _sagment;
		};
		$static = function(){
			var _declare = ejs.isObject(arguments[0])?arguments[0]:{};
			var _sagment = new __static_declare__();
			for(var p in _declare){
				_sagment.properties[p] = _declare[p];
			}
			return _sagment;
		};
		$package = $namespace = __package__;
		$import = $using = function(){
			for(var i=0;i<arguments.length;++i){
				if(ejs.isString(arguments[i]))
					__import_package__(arguments[i].trim());
			}
		};        
		$class = __class__;
		$interface = __interface__;
		/*********************** core end *********************/
		//utility class
		/************* package ejs.utils **************/
		$package('ejs.utils',
				 $class('ArrayUtil').$define(
					 $static({
						 foreach : function(/*array*/source, /*function*/fn, /*handle*/handle){
							 if(ejs.isArray(source) && ejs.isFunction(fn)){
								 for(var i=0;i<source.length;++i){
									 fn.call(handle||window, source[i]);
								 }
							 }
						 },
						 toArray : function(/*object*/source){
							 if(ejs.isString(source)){
								 return source.split(arguments[1]||',');
							 }
							 else if(ejs.isObject(source)){
								 var result = [];
								 for(var key in source){
									 result.push(source[key]);
								 }
								 return result;
							 }
						 }
					 })    
				 )
				);
		/******** package ejs.utils end****************/
		/************ package ejs.events **************/
		$package('ejs.events',
				 //event class
				 $class('Event').$define(
					 $public({
						 type:'',
						 relatedObject:null,
						 construct:function(){
							 (arguments[0]!=undefined) && (this.type = arguments[0]);
						 }
					 }),
					 $static({
						 INITIALIZE:'initialize-event',
						 COMPLETE:'complete-event'    
					 })
				 )
				);
		$package('ejs.events',
				 //event handle interface
				 $interface('IEventDispatcher').$define(
					 $protected({
						 eventListeners:{}    
					 }),
					 $public({
						 eventHandle : null,
						 construct : function(){
							 this._eventListeners = {};
							 this.eventHandle = this;
						 },
						 supportedEvents : function(){
							 var _eventName = [];
							 for(var e in this._eventListeners){
								 _eventName.push(e);
							 }
							 return _eventName;
						 },
						 addEventListener:function(/*string*/type, /*function*/listener){
							 if(!ejs.isFunction(listener))
								 return false;
							 var _eventListeners = this._eventListeners[type];
							 if(_eventListeners){
								 for(var i=_eventListeners.length-1;i>=0;--i){
									 if(_eventListeners[i]==listener)
										 return true;
								 }
								 this._eventListeners[type].push(listener);
							 }
							 else{
								 this._eventListeners[type] = [listener];
							 }
							 return true;
						 },
						 removeEventListener:function(/*string*/type, /*function*/listener){
							 var _eventListeners = this._eventListeners[type];
							 if(_eventListeners){
								 for(var i=_eventListeners.length-1;i>=0;--i){
									 if(_eventListeners[i]==listener){
										 this._eventListeners[type].splice(i,1);
									 }
								 }
							 } 
						 },
						 dispatchEvent:function(/*ejs.events.Event*/event){
							 if(event instanceof ejs.events.Event){
								 if(this._eventListeners[event.type]){
									 for(var i=0;i<this._eventListeners[event.type].length;++i){
										 event.relatedObject = this.eventHandle;
										 (this._eventListeners[event.type][i] || function(){}).call(this.eventHandle, event);
									 }
								 }
							 }
						 }
					 })        
				 )
				);
		/*********** package ejs.events end *************/
		/******************** package ejs.net *******************/
		$package('ejs.net',
				 //communication utility
				 $class('ResultEvent').$extends(ejs.events.Event).$define(
					 $public({
						 result:null  
					 }),
					 $static({
						 COMPLETE:'complete-result-event'    
					 })
				 ),
				 $class('FaultEvent').$extends(ejs.events.Event).$define(
					 $public({
						 fault:null,
						 errorCode:-1
					 }),
					 $static({
						 ERROR:'error-fault-event'    
					 })
				 ),
				 $class('ResultType').$define(
					 $static({
						 text:'text',
						 e4x:'xml',
						 xml:'xml'
					 })        
				 ),
				 $class('HttpMethod').$define(
					 $static({
						 post:'post',
						 get:'get'
					 })        
				 )
				);
		$package('ejs.net', 
				 $class('HttpService').$implements(ejs.events.IEventDispatcher).$define(
					 $protected({
						 httper:null,
						 createHttper:window.XMLHttpRequest?
							 function(){
								 return new window.XMLHttpRequest();
							 } :
						 function(){
							 try{
								 return new window.ActiveXObject("Microsoft.XMLHTTP");
							 } catch(e){}
						 },
						 onReadyStateChangeHandler:function(/*ejs.net.HttpService*/self){
							 return function(){
								 if(self._httper.readyState == 4){
									 if(self._httper.status == 200){
										 var resultEvent = new ejs.net.ResultEvent(ejs.net.ResultEvent.COMPLETE);
										 switch(self.resultType){
										 case ejs.net.ResultType.xml:
										 case ejs.net.ResultType.e4x:
											 resultEvent.result = self._httper.responseXml;
											 break;
										 case ejs.net.ResultType.text:
										 default:
											 resultEvent.result = self._httper.responseText;
										 }
										 self.dispatchEvent(resultEvent);
									 }
									 else{
										 var faultEvent = new ejs.net.FaultEvent(ejs.net.FaultEvent.ERROR);
										 faultEvent.fault = '[request error]';
										 faultEvent.errorCode = self._httper.status;
										 self.dispatchEvent(faultEvent);
									 }
								 }
							 };
						 }
					 }),
					 $public({
						 contentType : 'application/x-www-form-urlencoded',
						 method : ejs.net.HttpMethod.get,
						 resultType : ejs.net.ResultType.text,
						 url : '',
						 async : true,
						 construct : function(){
						 },
						 send : function(/*string*/param){
							 if(this.url){
								 this._httper = this._createHttper();
								 this._httper.onreadystatechange = this._onReadyStateChangeHandler(this);
								 this._httper.open(this.method, this.url, this.async);
								 this._httper.setRequestHeader('Content-Type', this.contentType);
								 this._httper.send(param);
							 }
						 }
					 })
				 )
				);
 /****** package ejs.net end*********/
	}
})();

