/**
 *	디지틀 조선일보 위젯 프레임
 *
 *	created by 제다, 2007.11
 *
 */
 
/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	디지틀 조선일보 위젯 프레임 기본정보 (버전,)
 */
var	DWFInfo = new function()
{
	this.majorVersion = 0;
	this.minorVersion = 1;
}


/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	전역변수 선언
 */
/** 모든 위젯영역은 고유의 ID값을 가지는데 이값을 생성하기 위한 구분값 */
var	__DWF_IDNUMBER = 0;
var	__DEF_IDSEGMENT = new Date().getTime();


/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	이벤트 정의
 */
function DWFEvent(source, eventType /*...*/)
{
	if ( arguments.length < 2 ) throw "Event객체를 생성하기에는 너무 적은 매개변수입니다.";
	
	this.source = source;
	this.eventType = eventType;
	
	// 만약 2개 이상의 매개변수가 있다면 파라미터로 갈무리한다.
	this.params = [];
	if ( arguments.length > 2 )
	{
		for ( var i = 2; i < arguments.length; i++ ) this.params[i - 2] = arguments[i];
	}
}

/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	이벤트 형태(Event Type)
 *
 *	이벤트 형태는 문자열로 구분한다. 문자열은 임의로 작성되어질 수 있으나, 아래와 같은 규칙으로 생성된다.
 *	규칙:event.name.[subname]
 *
 *	이 시스템에서 미리 정의된 이벤트형태는 아래와 같다. 또한 각각의 이벤트형태들은 서로 다른 매개변수들을 필요로 한다.
 *		- event.mouse.down			: 마우스 버튼누름
 *		- event.mouse.up			: 마우스 버튼누름 해제
 *		- event.container.start		: 위젯콘테이너가 구동요청됨
 *		- event.container.started	: 위젯콘테이너가 구동됨
 *		- event.container.stop		: 위젯콘테이너가 중단 요청됨
 *		- event.container.stopped	: 위젯콘테이너가 중단됨
 *		- event.layout.changed		: 레이아웃이 변경됨
 *		- event.window.create		: 위젯윈도우가 생성됨
 *		- event.window.close		: 위젯윈도우가 닫힘
 *		- event.window.show			: 위젯윈도우가 보이게 됨
 *		- event.window.hide			: 위젯윈도우가 숨겨짐
 *		- event.window.resized		: 위젯윈도우의 크기가 변경됨
 *		- event.window.minimized	: 위젯윈도우가 최소화됨
 *		- event.window.maximized	: 위젯윈도우가 최대화됨
 *		- event.window.selected		: 위젯윈도우가 선택됨
 *		- event.window.dragstart	: 위젯윈도우가 드래깅이 시작됨
 *		- event.window.dragging		: 위젯윈도우가 드래깅되고 있음
 *		- event.window.dragstop		: 위젯윈도우가 드래깅이 종료됨
 *		- event.window.newposition	: 위젯윈도우의 위치가 변경됨
 *
 */
var	DWFEventType = new function()
{
	this.mouse_down			= 0x0001;
	this.mouse_up			= 0x0002;
	this.container_start	= 0x0101;
	this.container_started	= 0x0102;
	this.container_stop		= 0x0103;
	this.container_stopped	= 0x0104;
	this.layout_changed		= 0x0201;
	this.window_create		= 0x0301;
	this.window_close		= 0x0302;
	this.window_show		= 0x0303;
	this.window_hide		= 0x0304;
	this.window_resized		= 0x0305;
	this.window_minimized	= 0x0306;
	this.window_maximized	= 0x0307;
	this.window_selected	= 0x0308;
	this.window_dragstart	= 0x0309;	// pt
	this.window_dragging	= 0x030A;	// pt
	this.window_dragstop	= 0x030B;	// pt
	this.window_newposition	= 0x030C;
	this.window_onicon		= 0x0401;
	this.window_onrefresh	= 0x0402;
	this.window_onoff		= 0x0403;
	this.window_onedit		= 0x0404;
	this.window_onclose		= 0x0405;
	
	// 구현사이트별로 사용자정의 이벤트일 경우에는 
	// 0xF000값부터 시작한다.
}

/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	이벤트 수신자
 */
var DWFEventListener = Class.create();

DWFEventListener.prototype =
{
	/**
	 *	초기화
	 *	@param listener: 이벤트를 수신하는 객체
	 *	@param feedbackFunc: 이벤트 발생시 호출될 이벤트를 수신하는 객체의 메소드
	 *	@param typeFilter: 이벤트 필터링 (null이면 모든 이벤트) (optional)
	 */
	initialize : function(listener, feedbackFunc, typeFilter)
	{
		this.listener = listener;
	}
	
	, listen : function(dwfEvent)
	{
	}
};


/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	이벤트 감시 핸들러
 *	마우스릴리즈등 화면전체를 대상으로 발생하는 마우스관련 이벤트를 분석한다.
 *
 */
function __dwfEventHandler(evt)
{
	try
	{
		//Event.stop(evt);
		
		switch(evt.type)
		{
			case "mousedown":
				if ( evt.target.dwfEventHandler != undefined ) evt.target.dwfEventHandler(evt, this);
				else
				{
					var	refPt = this.coord.calcEventXY(evt);	//콘테이너내 좌표를 구한다.
					var	refDiv = this.coord.findDiv(evt);		//관련 DIV영역을 구한다.
					
					if ( refDiv != null && evt.button == __BUTTON.left && refDiv.name.indexOf("DWF@DWFWindow:title") == 0 )
					{
						Event.stop(evt);
						//this.eventManager.dragSource = refDiv.parentNode;
						this.eventManager.raiseEvent(new DWFEvent(refDiv.parentNode, DWFEventType.window_dragstart, refPt));
						break;
					}
				}
				
				break;
				
			case "mousemove":
				if ( evt.button == __BUTTON.left && this.eventManager.dragSource != null )
				{
					Event.stop(evt);
					var	refPt = this.coord.calcEventXY(evt);
					this.eventManager.raiseEvent(new DWFEvent(this.eventManager.dragSource, DWFEventType.window_dragging, refPt, evt.target));
				}
				break;
				
			case "blur":
				break;
				
			case "mouseup":
				if ( this.eventManager.dragSource != null )
				{
					try
					{
						var	refPt = this.coord.calcEventXY(evt);
						this.eventManager.raiseEvent(new DWFEvent(this.eventManager.dragSource, DWFEventType.window_dragstop, refPt));
						this.eventManager.dragSource = null;
					}
					catch(e)
					{
					}
					finally
					{
						this.eventManager.dragSource = null;

						// 텍스트 선택등 이벤트 처리를 활성화 한다.
						//__enableTextSelection(refDiv, true);
						//__enableDragging(refDiv, true);
					}
				}
				break;
		}
	}
	
	catch(e)
	{
		alert("EXCEPTION:" + e);
	}
}

/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	이벤트 관리자
 */
var DWFEventManager = Class.create();

DWFEventManager.prototype =
{
	/** 이벤트 메인 발송자 */
	dispatcher : null
	/** 이벤트수신자들 */
	, listeners : null
	/** 드래깅 제어 변수: 드래깅 대상 */
	, dragSource : null 
	
	, initialize : function(dwfContainer)
	{
		var	refDiv = dwfContainer.divMe;
		this.listeners = new Hash();
		
		// 마우스릴리즈등 화면전체를 대상으로 감시할 이벤트들을 등록한다.
		//Event.observe(document.body, "mousemove", DWFEventManager.eyeMouse.bindAsEventListener(DWFEventManager));
		Event.observe(refDiv, "mousedown", __dwfEventHandler.bindAsEventListener(dwfContainer));
		Event.observe(document.body, "mouseup", __dwfEventHandler.bindAsEventListener(dwfContainer));
		Event.observe(document.body, "mousemove", __dwfEventHandler.bindAsEventListener(dwfContainer));
		Event.observe(document.body, "blur", __dwfEventHandler.bindAsEventListener(dwfContainer));
		
		// 텍스트 선택등 이벤트 처리를 비활성화 한다.
		__enableTextSelection(refDiv, false);
		__enableDragging(refDiv, false);		
	}
	
	/**
	 *	이벤트관리자를 등록한다.
	 *	@param target: 이벤트발생을 감시할 대상
	 *	@param listener: 이벤트발생시 호출되는 함수
	 */
	, addEventListener : function(target, listener)
	{
		// 
		var	lsnrs = this.listeners.get(target);
		if ( lsnrs == null ) 
		{
			lsnrs = [];
			this.listeners.set(target, lsnrs);
		}
		
		lsnrs[lsnrs.length] = listener;
	}
	
	/**
	 *	어떤 이벤트가 발생되었음을 통보받는다.
	 *	통보받은 이벤트를 분석한 후 필요한 추가행동이 있으면 실행하고,
	 *	해당 이벤트를 기다리는 이벤트수신자들에게 이벤트를 전달해준다.
	 *	@param evt:이벤트 (@see DWFEvent)
	 */
	, raiseEvent : function(dwfEvent)
	{
		try
		{
			// 이벤트 주 발송자가 있다면 관련 작업을 한다.
			if ( this.dispatcher != null )
			{
				this.dispatcher.dispatch(dwfEvent);
			}
			
			// 이벤트리스너가 있다면 관련작업을 한다.
			var	lsnrs = this.listeners.get(dwfEvent.source);
			for ( var i = 0; i < lsnrs.length; i++ ) lsnrs[i].listen(dwfEvent);
		}
		
		catch(e)
		{
		}
	}
	
};


/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	좌표계
 */
function DWFPoint(x, y)
{
	this.x = (x == undefined) ? 0 : x;
	this.y = (y == undefined) ? 0 : y;
}

var DWFCoordinate = Class.create();

DWFCoordinate.prototype =
{
	/** 기준이 되는 좌표DIV */
	refDiv : null
	//, refPt : null
	, basePt : null
	
	, initialize : function(refDiv)
	{
		this.refDiv = refDiv;
		
		var	arrBase = $(this.refDiv).cumulativeOffset();
		this.basePt = new DWFPoint(arrBase[0], arrBase[1]);
	}
	
	/**
	 *	이베트가 발생한 곳의 위젯프레임내 좌표값을 구한다.
	 *	@return 위젯프레임 기준 좌표값
	 */
	, calcEventXY : function(evt)
	{
		var absPt = new DWFPoint(Event.pointerX(evt), Event.pointerY(evt));
		absPt.x -= this.basePt.x;
		absPt.y -= this.basePt.y;

		return absPt;
	}
	
	/**
	 *	웹페이지내 좌표값을 위젯콘테이너 기준좌표값으로 변환해서 반환한다.
	 */
	, layer2Container : function(pt)
	{
		var	offset = (this.refDiv == null) ? new DWFPoint(0, 0) : new DWFPoint(parseInt(this.refDiv.offsetLeft), parseInt(this.refDiv.offsetTop));
		return new DWFPoint(pt.x - offset.x, pt.y - offset.y);
	}
	
	/**
	 *	위젯콘테이너 기준좌표값에서 웹페이지내 좌표값으로 변환해서 반환한다.
	 */
	, container2Layer : function(pt)
	{
		var	offset = (this.refDiv == null) ? new DWFPoint(0, 0) : new DWFPoint(parseInt(this.refDiv.offsetLeft), parseInt(this.refDiv.offsetTop));
		return new DWFPoint(pt.x + offset.x, pt.y + offset.y);
	}
	
	/**
	 *	이벤트와 관련된 DIV영역을 찾는다.
	 */
	, findDiv : function(evt)
	{
		var	target = evt.target;
		var	safeDepth = 0;
		
		while ( target != null )
		{
			if ( target.tagName != null && target.tagName.toLowerCase() == "div" && target.name != null && target.name.indexOf("DWF") == 0 )
				return target;
			
			target = target.parentNode;

			if ( ++safeDepth > 100 ) break;
		}
		
		return null;
	}
	
	/**
	 *	이벤트와 관련된 DWFWindow DIV영역을 찾는다.
	 */
	, findWindow : function(srcTarget)
	{
		var	target = srcTarget;
		var	safeDepth = 0;
		
		while ( target != null )
		{
			if ( target.tagName != null 
				&& target.tagName.toLowerCase() == "div" 
				&& target.name == DWFWindow.DIV_NAME )
				return target;
			
			target = target.parentNode;

			if ( ++safeDepth > 100 ) break;
		}
		
		return null;
	}
};
	
/**
 *	DOM객체가 콘테이너내에서 위치를 구한다.
 *	@param target:좌표를 구할 대상
 *	@param offsetPt:보정값
 */
DWFCoordinate.calcXY = function(domTarget, offsetPt)
{
	var absPt = (offsetPt == undefined) ? new DWFPoint(0, 0) : new DWFPoint(offsetPt.x, offsetPt.y);
	var	target = domTarget;
	var	safeDepth = 0;
	
	while ( target != null && !DWFContainer.isContainer(target) )
	{
		if ( target.tagName != null && target.tagName.toLowerCase() == "div" )
		{
			absPt.x += target.offsetLeft;
			absPt.y += target.offsetTop;
		}
		
		target = target.parentNode;
		
		if ( ++safeDepth > 100 ) throw "too much looping"
	}
	
	return absPt;
}


/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	DIV영역
 *	위젯콘테이너를 비로해서 많은 요소들이 <DIV>를 이용해서 구현이 된다.
 *	이 객체는 <DIV>를 제어하는 공통적으로 사용될 수 있고 기복적인 함수 및 속성을 지원한다.
 *
 *	DIV영역을 사용하는 구성요소는 반드시 <DIV>의 'name'속성값을 주어진 형식에 의해서 정의를 해야 한다.
 *	DWF:<class name>:<내부영역 명>
 *	예) DWF:DWFWindow:title
 */
var DivDockable = Class.create();

DivDockable.prototype =
{
	/** 구현자 이름, 재정의한 객체를 구분하기 위해서 사용한다. */
	dwfName : "unknown"
	, type : "unknown"
	
	/**
	 *	생성자
	 *	@param dwfName:구현자 이름
	 */
	, initialize : function(dwfName, type)
	{
		this.dwfName = dwfName;
		this.type = type;

		/** 생성된 <DIV> : 생성된 DIV영역은 해당객체가 소멸될 때까지 재정의 되지 않도록 한다. */
		this.divMe = document.createElement("div");
		this.divMe.id = "DWF__" + __DEF_IDSEGMENT + "-" + __DWF_IDNUMBER++;
		this.divMe.name = type;
		this.divMe.style.position = "relative";

		DWFDivMap.getInstance().setDwfComponent(this.divMe.id, this);
	}
	
	/**
	 *	DWFComponent의 ID를 변경한다.
	 */
	, setId: function(newId)
	{
		var	existId = DWFDivMap.getInstance().getDwfComponent(newId);
		if ( existId != undefined ) throw "이미 할당된 값입니다.";
		
		DWFDivMap.getInstance().unsetDwfComponent(this.divMe.id);
		DWFDivMap.getInstance().setDwfComponent(newId, this);
		this.divMe.id = newId;
	}
	
	/** <DIV> 폭을 반환한다. */
	, getWidth : function() 
	{
		if ( this.divMe == null ) return 0;
		 
	}
	
	/** <DIV> 높이를 반환한다. */
	, getHeight : function() { }
	
	/**
	 *	자신의 영역을 웹페이지의 DOM에 넣는다.
	 *	@param parentNode: 위치하게 될 부모노드
	 *	@param posNode: 위치 기준이  되는 노드로서 parentNode의 자식노드이다. (undefined이면 부모노드에 추가, 정의된 값이면 해당노드의 앞)
	 */
	, docking : function(parentNode, posNode)
	{
		try
		{
			if ( this.divMe != null && parentNode != null )
			{
				if ( posNode == undefined ) parentNode.appendChild(this.divMe);
				else
					parentNode.insertBefore(this.divMe, posNode);
			}
		}
		
		catch(e)
		{
		}
	}
	
	/**
	 *	자신의 영역을 웹페이지의 DOM에 넣는다.
	 */
	, undocking : function()
	{
		if ( this.divMe.parentNode != null ) this.divMe.parentNode.removeChild(this.divMe);
	}
	
	/**
	 *	이벤트 처리를 자신의 하위영역으로 발송한다.
	 *	@return 성공여부:true이면 성공 (반드시 결과값이 반환되어야 하며, 상속받은 객체는 부모객체의 수행결과를 항상 검사하여야 한다.)
	 */
	, dispatch : function(dwfEvent)
	{
		return true;
	}
	
	/**
	 *	콘테이너내에서 위치 좌표값을 구한다.
	 *	@return DWFPoint
	 */
	, getPosition : function()
	{
		return DWFCoordinate.calcXY(this.divMe, new DWFPoint(0, 0));
	}
	
	// implements 정의 : 아래 함수는 객체를 상속받는 쪽에서 꼭 함수를 추가후 구현해야 합니다.
	//, paint : function()
}


/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	위젯윈도우 구동기
 */
var DWFLauncher = Class.create();

DWFLauncher.prototype =
{
	/** 현재 구동중인 위젯윈도우들을 보관한다. */
	runnings: []
	/** namespace map, namespace의 상태는 0:미적재, 1:적재중, 2:적재완료*/
	, namespaceMap: null
	/** 스크립트가 적재되기를 기다리는 스택*/
	, waitLoad: null
	
	, initialize: function()
	{
		this.namespaceMap = new Hash();
		this.waitLoad = new Hash();
	}
	
	/**
	 *	위젯윈도우를 생성한다.
	 *	@param uri:위젯URI
	 */
	, launch : function(uri)
	{
		var	dwfWin = this.createWidgetWindow(uri);
		if ( dwfWin != null ) dwfWin.uri = uri;
		return dwfWin;
	}

	/**
	 *	구동중인 위젯을 멈춘다.
	 *	@param uri:위젯구분자
	 */
	, destroy : function(uri)
	{
	}

	/**
	 *	멈춰진 위젯에 대해서 통보를 받는다.
	 *	@param dwfWindow: 멈춰진 위젯윈도우
	 */
	, stopped : function(dwfWindow)
	{
	}
		
	, runNamespaceScript : function(namespace, uri, dwfWin)
	{
		// 스크립트가 적재되었는지 확인한다.
		var	status = this.namespaceMap.get(namespace);
		if ( status == undefined ) status = 0;
		
		try
		{
			switch(status)
			{
				case 0:
					// 적재되지 않았으면 적재작업을 시작한다.
					this.namespaceMap.set(namespace, 1);
					var	scriptLoader = new DWFNamespaceScriptLoader("", [uri], namespace);
					scriptLoader.load();

					//또한 대기목록에 등록되도록 한다.
					
				case 1:
					// 적재중이면 대기목록에 등록한다.
					var	waitList = this.waitLoad.get(namespace);
					if ( waitList == undefined ) this.waitLoad.set(namespace, [dwfWin]);
					else
						waitList[waitList.length] = dwfWin;
					break;
					
				case 2:
					// 이미적재중이면 구동한다.
					var namespaceInstance = eval("new " + namespace + "()");
					namespaceInstance.__wgt_onload(dwfWin, dwfWin.paramMap);
					dwfWin.namespace = namespaceInstance;
					dwfWin.onloadsuccess();
					break;
			}
				
		}
		catch(e)
		{
			dwfWin.onloadfail();
		}
	}
	
	, newNamespaceInstance : function(namespace)
	{
		this.namespaceMap.set(namespace, 2);
		
		var	waitList = this.waitLoad.get(namespace);
		if ( waitList == undefined ) return;
		
		for ( var i = 0; i < waitList.length; i++ )
		{
			var	dwfWin = waitList[i];
			try
			{
				var	namespaceInstance = eval("new " + namespace + "()");
				namespaceInstance.__wgt_onload(dwfWin, dwfWin.paramMap);
				dwfWin.namespace = namespaceInstance;
				dwfWin.onloadsuccess();
			}
			catch(e)
			{
				dwfWin.onloadfail();
			}
		}
	}
	
		 	
	// implements 정의 : 아래 함수는 콘테이너 객체를 상속받는 쪽에서 꼭 함수를 추가후 구현해야 합니다.
	//, createWidgetWindow : function()
	//, destroyWindow : function()
};

var DWFLauncher_SINGLETONE = null;

DWFLauncher.getInstance = function()
{
	return DWFLauncher_SINGLETONE;
}


/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	Widget Container
 *
 *	Widget Container는 ...
 */ 
var	DWFContainer = Class.create(DivDockable, {
	/** 위젯윈도우 구동기 */
	launcher : null
	/** 이벤트 관리자 */
	, eventManager : null
	/** 좌표계 */
	, coord : null
	
	, initialize : function($super)
	{
		$super("DWFContainer", DWFContainer.DIV_NAME);
		
		/** 위젯콘테이너가 위치할 외부페이지 DOM에서의 어느 한 객체(DIV등) */
		this.posInDOM = null;
		/** 위젯콘테이너에 위젯창의 위치를 제어할 레이아웃 */
		this.layout = null;
	}
	
	/**
	 *	위젯콘테이너를 구동한다.
	 *	@param posInDOM:위젯콘테이너가 구동할 위부페이지 DOM에서의 위치 (통상 DIV객체가 된다)
	 */
	, start : function(posInDOM)
	{
		this.posInDOM = posInDOM;
		this.paint();
		this.docking(posInDOM);

		/** 위젯구동기 전역으로 한개만 선언해서 사용한다. (namespace관리)*/
		this.launcher = new DWFLauncher();
		DWFLauncher_SINGLETONE = this.launcher;

		// 이벤트 매니저는 전역으로 한개만 선언해서 사용한다.
		this.eventManager = new DWFEventManager(this);
		// 이벤트매니저에 자신을 주 발송자로 등록한다.
		this.eventManager.dispatcher = this;
		
		// 좌표계에 기준이 되는 자신을 등록한다.  전역으로 한개만 선언해서 사용한다.
		this.coord = new DWFCoordinate(this.divMe);
		//this.coord.refDiv = this.divMe;
	}
	
	/**
	 *	위젯콘테이너를 중단한다.
	 */
	, stop : function()
	{
		try
		{
			this.eventManager.raiseEvent(new DWFEvent(this, DWFEventType.container_stop));
		}
		
		catch(e)
		{
		}
		
		finally
		{
			this.undocking();
		}
	}
	
	/**
	 *	레이아웃을 설정한다.
	 *	직접 레이아웃을 위젯콘테이너 property값에 할당하지 않고, 꼭 이함수를 통하도록 한다.
	 */
	, setLayout : function(layout)
	{
		try
		{
			this.layout = layout;	//먼저 연결을 해준다. 왜냐면 아래함수 구현을 편하게 하기 위하여
			layout.boundContainer(this);
		}
		catch(e)
		{
			this.layout = null;
			throw e;
		}
	}
	
	/**
	 *	레이아웃을 반환한다.
	 */
	, getLayout : function()
	{
		return this.layout;
	}
	
	/**
	 *	위젯을 생성한다.
	 *	레이아웃에는 추가하지 않고 위도우만 생성한다. 위젯의 적재는 이 함수를 호출한 곳에서 책임진다.
	 *	@param uri:위젯URI
	 *	@param params:매개변수값 묶음 Hash,optional
	 */
	, createWidget : function(uri, paramMap)
	{
		if ( this.layout == null ) throw "위젯콘테이너에 레이아웃이 설정되지 않았습니다.";
		
		var	dwfWin = this.launcher.launch(uri);		
		if ( paramMap != undefined ) dwfWin.paramMap = paramMap;
		return dwfWin;
	}

	/**
	 *	위젯을 추가한다.
	 *	레이아웃에도 같은 함수가 있지만 위젯콘테이너의 함수를 사용하도록 한다.
	 *	@param uri:위젯URI
	 *	@param params:매개변수값 묶음 Hash,optional
	 */
	, addWidget : function(uri, paramMap)
	{
		if ( this.layout == null ) throw "위젯콘테이너에 레이아웃이 설정되지 않았습니다.";
		
		var	dwfWin = this.launcher.launch(uri);
		if ( paramMap != undefined ) dwfWin.paramMap = paramMap;

		this.layout.addWidgetWindow(dwfWin);
		dwfWin.loadWidget(uri);
	}
	
	/**
	 *	위젯을 제거한다.
	 *	레이아웃에도 같은 함수가 있지만 위젯콘테이너의 함수를 사용하도록 한다.
	 */
	, removeWidget : function(uri)
	{
		if ( this.layout == null ) throw "위젯콘테이너에 레이아웃이 설정되지 않았습니다.";
		var	found = this.layout.findWidgetWindowByUri(uri);
		if ( found.length == 0 ) throw "위젯을 찾지 못했습니다.";
		
		// 직접닫지를 않고 이벤트 처리를 한다.
		for ( var i = 0; i < found.length; i++ ) 
		{
			this.eventManager.raiseEvent(new DWFEvent(found[i].divMe, DWFEventType.window_onclose));
		}
	}
	
	/**
	 *	이벤트 처리를 자신의 하위영역으로 발송한다.
	 */
	, dispatch : function($super, dwfEvent)
	{
		if ( $super() && this.layout != null ) return this.layout.dispatch(dwfEvent);
		else
			return false;
	}
	
	// implements 정의 : 아래 함수는 객체를 상속받는 쪽에서 꼭 함수를 추가후 구현해야 합니다.
	//, paint : function()
});



// 기타 상수 선언 및 static함수 선언
/** DIV영역 정의시 사용할 이름 */
DWFContainer.DIV_NAME = "DWF@DWFContainer";

/**
 *	해당객첵가 DWFContainer용 <DIV>인지 판단한다.
 */
DWFContainer.isContainer = function(target)
{
	return (target == null || target.name == null || target.name != DWFContainer.DIV_NAME) ? false : true;
}

/** DIV영역과 DWFContainer를 연결해둔다 */
//DWFContainer.div2Containers = new Hash();

/**
 *	매개변수로 넘어온 DIV이 속한 DWFContainer를 찾는다.
 *	@param fromDiv:찾는위치
 *	@return 찾으면 해당 DWfContainer객체 없으면 null
 */
DWFContainer.findContainer = function(fromDiv)
{
	var	target = fromDiv;
	var	safeDepth = 0;
	
	while ( target != null )
	{
		if ( DWFContainer.isContainer(target) )
		{
			return DWFDivMap.getInstance().getDwfComponent(target.id);
		}
		
		target = target.parentNode;
		
		if ( ++safeDepth > 100 ) throw "too much looping"
	}
	
	return null;
}

/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	Widget Layout
 */
var DWFLayout = Class.create(DivDockable, {
	/** 레이아웃을 포함한 콘테이너 */
	container: null
	/** 위젯윈도우 객체와 위젯윈도우 영역(<DIV>)를 연결 */
	, widgetMap: new Hash()
	
	, initialize : function($super)
	{
		$super("DWFLayout", DWFLayout.DIV_NAME);
	}
	
	/**
	 *	위젯콘테이너와 연결한다.
	 *	@param widgetContainer:레이아웃이 할당되는 위젯콘테이너
	 */
	, boundContainer : function(widgetContainer)
	{
		this.container = widgetContainer;
		this.docking(this.container.divMe);	//레이아웃을 그리기전에 연결해야 좌표값이 정확히 먹는다.
		this.paint();
		
		// 기본적
	}
	
	/**
	 *	위젯윈도우를 추가한다.
	 *	@param dwfWin:위젯윈도우
	 */
	, addWidgetWindow : function(dwfWin)
	{
		this.widgetMap.set(dwfWin.divMe.id, dwfWin);
	}
	
	/**
	 *	위젯위도우를 제거한다.
	 */
	, removeWidgetWindow : function(dwfWin)
	{
		this.widgetMap.unset(dwfWin.divMe.id);
		DWFDivMap.getInstance().unsetDwfComponent(dwfWin.divMe.id);
		dwfWin.undocking();
	}
	
	/**
	 *	위젯위도우를 찾는다.
	 */
	, getWidgetWindow : function(id)
	{
		return this.widgetMap.get(id);
	}
	
	/**
	 *	URI를 이용해서 위젯위도우를 찾는다.
	 *	@param uri:찾는 URI
	 *	@return 찾아진 DWFWindow배열, 없으면 빈배열
	 */
	, findWidgetWindowByUri : function(uri)
	{
		var	found = [];
		var	values = this.widgetMap.values();
		
		for ( var i = 0; i < values.length; i++ )
		{
			if ( values[i].uri == uri ) found[found.length] = values[i];
		}
		
		return found;
	}
	
	/**
	 *	이벤트 처리를 자신의 하위영역으로 발송한다.
	 */
	, dispatch : function($super, dwfEvent)
	{
		try
		{
			if ( !$super() ) return false;	
			var	dwfWin = null;
			
			switch(dwfEvent.eventType)
			{
				case DWFEventType.window_dragstart: 
					dwfWin = this.widgetMap.get(dwfEvent.source.id);
					if ( dwfWin == null || dwfWin.isStatic ) return false;
					else
					{
						dwfWin.ondragstart(dwfEvent.params[0]);
						this.container.eventManager.dragSource = dwfWin.divMe;
					}
					break;
				
				case DWFEventType.window_dragging: 
					dwfWin = this.widgetMap.get(dwfEvent.source.id);
					if ( dwfWin == null ) return false;
					else
						dwfWin.ondragging(dwfEvent.params[0]);
					break;
					
				case DWFEventType.window_dragstop: 
					dwfWin = this.widgetMap.get(dwfEvent.source.id);
					if ( dwfWin == null ) return false;
					else
						dwfWin.ondragstop(dwfEvent.params[0]);
					break;
					
				default: 
					dwfWin = this.widgetMap.get(dwfEvent.source.id);
					if ( dwfWin == null ) return false;
					else
						dwfWin.onevent(dwfEvent.eventType);
					break;
			}
			
			return true;
		}
		
		catch(e)
		{
		}
		
		return false;
	}
	
	// implements 정의 : 아래 함수는 객체를 상속받는 쪽에서 꼭 함수를 추가후 구현해야 합니다.
	//, paint : function()
});

// 기타 상수 선언
/** DIV영역 정의시 사용할 이름 */
DWFLayout.DIV_NAME = "DWF@DWFLayout";

/**
 *	해당객첵가 DWFLayout용 <DIV>인지 판단한다.
 */
DWFLayout.isLayout = function(target)
{
	return (target == null || target.name == null || target.name != DWFLayout.DIV_NAME) ? false : true;
}

/**
 *	매개변수로 넘어온 DIV이 속한 DWFLayout을 찾는다.
 *	@param fromDiv:찾는위치
 *	@return 찾으면 해당 DWFLayout객체 없으면 null
 */
DWFLayout.findLayout = function(fromDiv)
{
	var	target = fromDiv;
	var	safeDepth = 0;
	
	while ( target != null )
	{
		if ( DWFLayout.isLayout(target) )
		{
			return DWFDivMap.getInstance().getDwfComponent(target.id);
		}
		
		target = target.parentNode;
		
		if ( ++safeDepth > 100 ) throw "too much looping"
	}
	
	return null;
}


/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 * Widget Window
 */
var	DWFWindowState = new function()
{
	this.minimized = 0x0010;
	this.maximized = 0x0020;
}

var DWFWindow = Class.create(DivDockable, {

	/** 위젯의 URI */
	uri : null
	/** 위젯적재 여부 */
	, isLoaded : false
	/** 위젯함수 namespace */
	, namespace : null
	/** xmldoc */
	, xmlDoc : null
	/** 구동 매개변수 */
	, paramMap : null
	/** 창제목 */
	, windowTitle : ""
	/** 창아이콘 */
	, windowIcon : ""
	/** 윈도우 상태를 설정한다. */
	, windowState : 0x0000
	/** 위젯 윈도우의 타이틀 */
	, divTitle : null
	/** 타이틀를 제외한 영역 */
	, divContent : null
	/** 실제로 위젯이 놓이는 영역 */
	, divClient : null
	
	, initialize: function($super)
	{
		$super("DWFWindow", DWFWindow.DIV_NAME);
		this.paramMap = new Hash();
	}
	
	/**
	 *	위젯함수를 할당한다.
	 */
	, setWidgetFunctions: function(namespace)
	{
		this.namespace = namespace;
	}

	/**
	 *	특정 위젯을 적재한다.
	 *	@param uri:위젯 위치
	 */
	, loadWidget: function(uri)
	{
		this.uri = uri;

		if ( !this.isLoaded )
		{
			var myReq = new Ajax.Request(uri,
			{
				method: "get"
				, onSuccess: DWFWindow_AJAX_ONSUCCESS.bind(this)
				, onFailure: this.onloadfail.bind(this)
			})
		}
	}

	/**
	 *	적재한 위젯을 해제한다. 화면상의 내용도 지운다.
	 */
	, unloadWidget: function()
	{
	}

	/**
	 *	위젯윈도우를 닫는다.
	 */
	, close: function()
	{
		var	layout = DWFLayout.findLayout(this.divMe);
		layout.removeWidgetWindow(this);
	}
	
	/**
	 *	위젯용 저장소를 제공한다.
	 */
	, getRepository: function()
	{
		throw "저장소를 지원하지 않습니다.";
	}

	
	/** 창을 그린다. */
	, paint: function() { }
	/** 창을 다시 그린다. */
	, redraw: function() { }
	
	/** 창제목을 변경한다. */
	, setWindowTitle: function(title) { this.windowTitle = title; }
	/** 창아이콘을 변경한다. */
	, setWindowIcon: function(windowIcon) { this.windowIcon = windowIcon; }
	/** 창색상을 변경한다. */
	, setWindowColor: Prototype.emptyFunction()
	/** 창색상을 변경한다. */
	, setWindowBorderColor: Prototype.emptyFunction()
	/** 위젯이 탑재되는 클라이언트 영역의 크기를 구한다. */
	, getClientRect: function() { return [0, 0]; }
	 
	// event handlers //////////////////////////////////////////////////////////
	, onloadsuccess : function() { this.isLoaded = true; this.onevent(DWFEventType.window_resized); }
	, onloadfail : function() { alert("위젯을 호출하는 도중 오류가 발생하였습니다."); this.close(); }
	, ondragstart : function(pt) { }
	, ondragstart : function(pt) { }
	, ondragging : function(pt) { }
	, ondragstop : function(pt) { }
	, onevent : function(evtType)
	{
		if ( !this.isLoaded ) return;
		
		switch(evtType)
		{
			case DWFEventType.window_onclose:
				if ( this.namespace != undefined ) this.namespace.__wgt_onclose();
				this.close();
				break;
			case DWFEventType.window_minimized:
				this.divContent.style.display = "none";
				this.windowState |= DWFWindowState.minimized;
				this.windowState &= ~DWFWindowState.maximized;
				break;
			case DWFEventType.window_maximized:
				this.divContent.style.display = "block";
				this.windowState |= DWFWindowState.maximized;
				this.windowState &= ~DWFWindowState.minimized;
				break;
				
			default:
				if ( this.namespace != undefined && this.namespace.__wgt_onevent != undefined ) 
					this.namespace.__wgt_onevent(evtType);
		}
	}
});

function DWFWindow_AJAX_ONSUCCESS(transport)
{
	try
	{
		this.xmlDoc = transport.responseXML;
	
		var	namespace = this.xmlDoc.getElementsByTagName('namespace')[0].firstChild.data;
		var	bootstrap = this.xmlDoc.getElementsByTagName('bootstrap')[0];
		var	basePath = this.uri.substring(0, this.uri.lastIndexOf("/") + 1);
		var	bootJs = bootstrap.getElementsByTagName('url')[0].firstChild.data;
		
		// 매개변수를 구한다.
		var	arrParam = bootstrap.getElementsByTagName('param');
		for ( var i = 0; i < arrParam.length; i++ ) 
		{
			if ( arrParam[i].firstChild != null )
				this.paramMap.set(arrParam[i].getAttribute("name"), arrParam[i].firstChild.data);
		}
		
		DWFLauncher.getInstance().runNamespaceScript(namespace, basePath + bootJs, this);
	}
	catch(e)
	{
		this.onloadfail();
	}
}

// 기타 상수 선언
/** DIV영역 정의시 사용할 이름 */
DWFWindow.DIV_NAME = "DWF@DWFWindow";


/** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 * Widget Namespace
 */
var DWFNamespaceScriptLoader = Class.create(__ScriptLoader, {

	namespace : null
	
	, initialize : function($super, urlPrefix, scripts, namespace)
	{
		this.urlPrefix = urlPrefix;
		this.scripts = scripts;
		this.namespace = namespace;
	}

	, sucess : function($super)
	{
		DWFLauncher.getInstance().newNamespaceInstance(this.namespace);
	}
	
	, fail : function($super)
	{
		this.dwfWin.onloadfail();
	}

});

var DWFDivMap = Class.create();
DWFDivMap.prototype =
{
	divMap : null
	
	, initialize : function()
	{
		this.divMap = new Hash();
	}
	
	, getDwfComponent : function(divId)
	{
		return this.divMap.get(divId);
	}
	
	, setDwfComponent : function(divId, dwfComponent)
	{
		return this.divMap.set(divId, dwfComponent);
	}
	
	, unsetDwfComponent : function(divId)
	{
		return this.divMap.unset(divId);
	}
};

var DWFDivMap_SINGLETONE = new DWFDivMap();
DWFDivMap.getInstance = function()
{
	return DWFDivMap_SINGLETONE;
}

var DWFAPI = Class.create();

/** 위젯이 사용하는 특화된 API를 정의한다. */
DWFAPI.CUSTOMAPI = undefined;
/** 위젯이 사용하는 특화된 상수값을 정의한다. */
DWFAPI.CUSTOMCONST = undefined;

/** 
 * 현재 위치를 포함하는 DWFWindow객체를 구한다.
 */
DWFAPI.getWindow = function(fromDiv)
{
	var	target = fromDiv;
	var	safeDepth = 0;
	
	while ( target != null )
	{
		if ( target.name != null && target.name.indexOf(DWFWindow.DIV_NAME) == 0 )
		{
			return DWFDivMap.getInstance().getDwfComponent(target.id);
		}
		
		target = target.parentNode;
		
		if ( ++safeDepth > 100 ) throw "too much looping"
	}
	
	return null;
}


/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *	저장소
 */
var DWFRepository = Class.create();
DWFRepository.prototype =
{
	saveXML: function() { }
	
	, loadXML: function() { }
	
	// 이 객체를 상속받아 구현하는 개체는 반드시 아래 함수들을 정의하고 구현해야 한다.
	/*
	, save
	, load
	*/
}