/**
 * 
 * @author Hahm Myung Sun (hms1475@gmail.com)
 *
 * Copyright (c) 2011 JinoTech (http://www.jinotech.com) 
 * Licensed under the LGPL v3.0 license (http://www.gnu.org/licenses/lgpl.html).
 */

// Events
// 이벤트 모음 (jNode, document 에서 사용)
var preventDefault = function () {
    this.returnValue = false;
},
preventTouch = function () {
    return this.originalEvent.preventDefault();
},
stopPropagation = function () {
    this.cancelBubble = true;
},
stopTouch = function () {
    return this.originalEvent.stopPropagation();
},
addEvent = (function () {
    if (document.addEventListener) {
        return function (obj, type, fn, element) {
            var realName = supportsTouch && touchMap[type] ? touchMap[type] : type;
            var f = function (e) {
                if (supportsTouch && touchMap.hasOwnProperty(type)) {
                    for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
                        if (e.targetTouches[i].target == obj) {
                            var olde = e;
                            e = e.targetTouches[i];
                            e.originalEvent = olde;
                            e.preventDefault = preventTouch;
                            e.stopPropagation = stopTouch;
                            break;
                        }
                    }
                }
                return fn.call(element, e);
            };
            obj.addEventListener(realName, f, false);
            return function () {
                obj.removeEventListener(realName, f, false);
                return true;
            };
        };
    } else if (document.attachEvent) {
        return function (obj, type, fn, element) {
            var f = function (e) {
                e = e || win.event;
                e.preventDefault = e.preventDefault || preventDefault;
                e.stopPropagation = e.stopPropagation || stopPropagation;
                return fn.call(element, e);
            };
            obj.attachEvent("on" + type, f);
            var detacher = function () {
                obj.detachEvent("on" + type, f);
                return true;
            };
            return detacher;
        };
    }
})();

////////////////////////



///**
// * 
// * @param {jNode} node
// * @param {bool} copyid : 아이디도 같이 복사
// */
//function copyNodes(node, copyid){
//	var data = {};
//	// body
//	var bodyAttr = node.body.attr();
//	delete bodyAttr.scale;
//	delete bodyAttr.translation;
//	delete bodyAttr["fill-opacity"];	
//	delete bodyAttr.gradient;
//	bodyAttr.fill = node.background_color
//	bodyAttr.stroke = node.edge.color;
//	bodyAttr["stroke-width"] = node.stroke_width;
//	data.body = bodyAttr;
//	// text 
//	var textAttr = node.text.attr();
//	delete textAttr.scale;
//	delete textAttr.translation;
//	data.text = textAttr;
//	// folderShape;
//	var folderAttr = node.folderShape.attr();
//	delete folderAttr.scale;
//	delete folderAttr.translation;
//	delete folderAttr["fill-opacity"];
//	data.folderShape = folderAttr;
//	// hyperlink
//	data.hyperlink = node.hyperlink && node.hyperlink.attr().href;
//	// img
//	data.img = node.img && node.img.attr().src;
//	// note
//	data.note = node.note;
//	// FreeMind Node 속성
//	data.background_color = node.background_color;
//	data.color = node.color;
//	data.folded = node.folded;
//	//data.id = node.id;				// 아이디는 필요시 나중에 복사 됨
//	data.plainText = node.plainText;
//	data.link = node.link;
//	//data.position = node.position;	// position은 루트에서 결정되므로 복사안함
//	data.style = node.style;
//	data.created = node.created;
//	data.modified = node.modified;
//	//data.hgap = node.hgap;
//	//data.vgap = node.vgap;
//	//data.vshift = node.vshift;
//	// Layout을 위한 속성
//	//data.SHIFT = node.SHIFT;
//	//data.relYPos = node.relYPos;
//	//data.treeWidth = node.treeWidth;
//	//data.treeHeight = node.treeHeight;
//	//data.leftTreeWidth = node.leftTreeWidth;
//	//data.rightTreeWidth = node.rightTreeWidth;
//	//data.upperChildShift = node.upperChildShift;
//	// edge 속성
//	data.edge = node.edge;
//	data.stroke_width = node.stroke_width;
//	data.fontSize = node.fontSize;
//	// foreignObject
//	if(node.foreignObjEl){		
//		data.foreignObject_plainHtml = node.foreignObjEl.plainHtml;
//		data.foreignObject_width = node.foreignObjEl.getAttribute("width");
//		data.foreignObject_height = node.foreignObjEl.getAttribute("height");
//	}
//	
//	if(copyid) data.id = node.id;
//	data.child = new Array;
//	
//	if(node.getChildren().length > 0) {
//		var children = node.getChildren();
//		for(var i = 0; i < children.length; i++) {
//			data.child.push(copyNodes(children[i], copyid));
//		}
//	}
//	
//	return data;
//}
//
//function pasteNodes(data, /*jNode*/parentNode, index, position){
//	// 잘라낸 노드의 자식을 잘라낸경우 삭제
//	if(data.body.removed) return;
//	
//	var id;
//	if(!parentNode) parentNode = jMap.getSelecteds().getLastElement();	
//	if(data.id) id = data.id;
//	var newNode = jMap.createNodeWithCtrlExecute(parentNode, "", id, index, position);
//	parentNode.folded && parentNode.setFoldingExecute(parentNode.folded);
//	newNode.body.attr(data.body);
//	newNode.text.attr(data.text);
//	newNode.folderShape.attr(data.folderShape);	
//	data.hyperlink && newNode.setHyperlinkExecute(data.hyperlink);
//	data.img && newNode.setImageExecute(data.img);
//	// note
//	newNode.note = data.note;
//	// FreeMind Node 속성
//	newNode.background_color = data.background_color;
//	newNode.color = data.color;
//	newNode.folded = data.folded;
//	//newNode.id = data.id;				// 아이디는 노드 만들어 질때 만들어짐
//	newNode.plainText = data.plainText;
//	newNode.link = data.link;
//	//newNode.position = data.position;	// postion은 복사하면 안됨
//	newNode.style = data.style;
//	newNode.created = data.created;
//	newNode.modified = data.modified;
//	//newNode.hgap = data.hgap;
//	//newNode.vgap = data.vgap;
//	//newNode.vshift = data.vshift;
//	// Layout을 위한 속성
//	//newNode.SHIFT = data.SHIFT;
//	//newNode.relYPos = data.relYPos;
//	//newNode.treeWidth = data.treeWidth;
//	//newNode.treeHeight = data.treeHeight;
//	//newNode.leftTreeWidth = data.leftTreeWidth;
//	//newNode.rightTreeWidth = data.rightTreeWidth;
//	//newNode.upperChildShift = data.upperChildShift;
//	// edge 속성
//	newNode.edge = data.edge;
//	newNode.stroke_width = data.stroke_width;
//	newNode.fontSize = data.fontSize;
//	// foreignObject
//	if(data.foreignObject_plainHtml){		
//		newNode.setForeignObjectExecute(data.foreignObject_plainHtml, 
//				data.foreignObject_width, data.foreignObject_height);
//	}
//	
//	if(data.child.length > 0){
//		for(var i=0; i < data.child.length; i++){
//			pasteNodes(data.child[i], newNode);
//		}
//	}
//	
//	jMap.setSaved(false);
//	
//	return newNode;
//}

// 키코드 체크
function F_CheckKey(evt, codes) {
	evt = (evt) ? evt:window.event;
	code = (evt.keyCode)? evt.keyCode:evt.charCode;
	
	for ( var i=0; i<codes.length; i++ ) {
		if ( codes[i] == code ) {
			return true;
		}
	}
	return false;
}

///////////////////////////////////////////////////////////////////////////////
/////////////////////////// JinoController ////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

JinoController = function (map){
	this.map = map;
	this.nodeEditor = null;
	
	// 키 이벤트 제어
	document.onkeydown = function(evt){
		evt = evt || window.event;		
		if(jMap.work.hasFocus())
			return false;		
		return true;
	};
	
	// 노드 편집창 설정
	this.setNodeEditor(map.nodeEditorHandle[0]);
	
	// 마우스 이벤트
	this.map.mousemove(this.mousemove);
	this.map.mousedown(this.mousedown);
	this.map.mouseup(this.mouseup);
	this.map.work.onkeydown = this.keyDown;
	
	// 노드 이외의 공간에 드래그앤 드롭 이벤트.. 모두 막기
	this.map.work.ondragenter = function(e){
		e = e || window.event;
		if (e.preventDefault)
			e.preventDefault();
		else
			e.returnValue= false;
	};
	this.map.work.ondragover = function(e) {
		e = e || window.event;
		if (e.preventDefault)
			e.preventDefault();
		else
			e.returnValue= false;
	};
	this.map.work.ondrop = function(e){
		e = e || window.event;
		if (e.preventDefault)
			e.preventDefault();
		else
			e.returnValue= false;		
	};
	
	// wheel event
	/**
	 * This is high-level function. It must react to delta being more/less than
	 * zero.
	 */
	function handle(delta) {
		if (delta < 0){
			if(jMap.scaleTimes < 0.2) return;
			jMap.scale(jMap.scaleTimes-0.1);
		} else {
			if(jMap.scaleTimes > 2.5) return;			
			jMap.scale(jMap.scaleTimes+0.1);			
		}
	}
	
	/**
	 * Event handler for mouse wheel event.
	 */
	function wheel(event){
		var delta = 0;
		if (!event) /* For IE. */
			event = window.event;
		if (event.wheelDelta) { /* IE/Opera. */
			delta = event.wheelDelta/120;
			/**
			 * In Opera 9, delta differs in sign as compared to IE.
			 */
			if (window.opera)
				delta = -delta;
		} else if (event.detail) { /** Mozilla case. */
			/**
			 * In Mozilla, sign of delta is different than in IE. Also, delta is multiple of
			 * 3.
			 */
			delta = -event.detail/3;
		}
		/**
		 * If delta is nonzero, handle it. Basically, delta is now positive if wheel was
		 * scrolled up, and negative, if wheel was scrolled down.
		 */
		if (delta)
			handle(delta);
		/**
		 * Prevent default actions caused by mouse wheel. That might be ugly, but we
		 * handle scrolls somehow anyway, so don't bother here..
		 */
		if (event.preventDefault)
			event.preventDefault();
		event.returnValue = false;
	}
	
	/**
	 * Initialization code. If you use your own event management code, change it as
	 * required.
	 */
	if (window.addEventListener)
	/** DOMMouseScroll is for mozilla. */
	window.addEventListener('DOMMouseScroll', wheel, false);
	/** IE/Opera. */
	window.onmousewheel = document.onmousewheel = wheel;
	
	
	// 세션 유지를 위해서
	//this.map.setSessionTimeout();
}

JinoController.prototype.type= "JinoController";

JinoController.prototype.keyDown = function(evt) {
	if ( STAT_NODEEDIT ) return true;
	
	evt = evt || window.event;
	
	// ctrl 및 meta 키
	var ctrl = null; 
    if (evt) ctrl = evt.ctrlKey; 
    else if (evt && document.getElementById) ctrl=(Event.META_MASK || Event.CTRL_MASK)
    else if (evt && document.layers) ctrl=(evt.metaKey || evt.ctrlKey);
    // alt 키
    var alt = evt.altKey
	
	var code = evt.keyCode;
	switch( code ) {
		case 35:	// END
			if (alt) {
				jMap.controller.unfoldingAllAction();
			}
		break;
		case 36:	// HOME
			if (alt) {
				jMap.controller.foldingAllAction();
			}
		break;
		case 37:	// LEFT
			if (ctrl){
				ScaleAnimate.prevShow(30);
				return false;
			}
			var selected = jMap.getSelecteds().getLastElement();
			switch(jMap.layoutManager.type) {
				case "jMindMapLayout" :
					if(selected.isRootNode()) {
						var children = selected.getChildren();
						for(var i=0; i < children.length; i++){
							if (children[i].position == "left") {
								children[i].focus(true);
								break;
							}											
						}
					} else if(selected.isLeft()) {
						jMap.controller.childNodeFocusAction(selected);
					} else {
						jMap.controller.parentNodeFocusAction(selected);
					}
					break;
				case "jTreeLayout" :
					jMap.controller.prevSiblingNodeFocusAction(selected);					
					break;
				default :
			}
		break;
		case 38:	// UP
			if (ctrl){
				ScaleAnimate.tempRotate(jMap.getSelecteds().getLastElement());
				return false;
			}
			var selected = jMap.getSelecteds().getLastElement();
			switch(jMap.layoutManager.type) {
				case "jMindMapLayout" :
					if (selected.isRootNode()) {
						
					} else {
						jMap.controller.prevSiblingNodeFocusAction(selected);
					}
					break;
				case "jTreeLayout" :
					jMap.controller.parentNodeFocusAction(selected);
					break;
				default :
			}
		break;
		case 39:	// RIGHT
			if (ctrl){
				ScaleAnimate.nextShow(30);
				return false;
			}
			var selected = jMap.getSelecteds().getLastElement();
			switch(jMap.layoutManager.type) {
				case "jMindMapLayout" :
					if(selected.isRootNode()) {
						var children = selected.getChildren();
						for(var i=0; i < children.length; i++){
							if (children[i].position == "right") {
								children[i].focus(true);
								break;
							}											
						}
					} else if(selected.isLeft()) {
						jMap.controller.parentNodeFocusAction(selected);				
					} else {
						jMap.controller.childNodeFocusAction(selected);
					}
					break;
				case "jTreeLayout" :
					jMap.controller.nextSiblingNodefocusAction(selected);
					break;
				default :
			}
		break;
		case 40:	// DOWN
			var selected = jMap.getSelecteds().getLastElement();
			switch(jMap.layoutManager.type) {
				case "jMindMapLayout" :
					if (selected.isRootNode()) {
						
					} else {
						jMap.controller.nextSiblingNodefocusAction(selected);
					}
					break;
				case "jTreeLayout" :
					jMap.controller.childNodeFocusAction(selected);
					break;
				default :
			}
		break;
		
		case 49:	// '1'
			if (ctrl){
				NodeColorMix(jMap.rootNode);
			}
		break;
		case 50:	// '2'
			if (ctrl){
				var calcWidth = function(node, widths, index) {					
					var width = node.body.getBBox().width;
					if(!widths[index]) widths[index] = 0;
					widths[index] = (width > widths[index])? width : widths[index];
					
					if(node.getChildren().length > 0) {
						var children = node.getChildren();
						index++;
						for(var i = 0; i < children.length; i++) {
							calcWidth(children[i], widths, index);						
						}
					}
					
				}
				
				var maxWidth = [];
				calcWidth(jMap.getRootNode(), maxWidth, 0);
				
				console.log(maxWidth);
			}
		break;
		case 51:	// '3'
			if (ctrl){
			}
		break;
		case 52:	// '4'
			if (ctrl){
			}
		break;
		case 53:	// '5'
			if (ctrl){
				console.log(DeliciousService.session());
			}
		break;
		case 54:	// '6'
			if (ctrl){
				jMap.cfg.realtimeSave = false;
				DeliciousService.init(jMap);
			}
		break;
		case 55:	// '7'
			if (ctrl){
				DeliciousService.logout();
			}
		break;
		case 65:	// 'a'			
			if (ctrl){
				
				alert(jMap.getSelected().id);				
				
			}
		break;
		case 67:	// 'c'
			if (ctrl) {
				jMap.controller.copyAction();
			}
		break;		
		case 70:	// 'f'
			if (ctrl){				
				jMap.controller.findNodeAction();
			}
		break;
		case 71:	// 'g' google seach on/off
			if ( ctrl ) {
				if(AL_GOOGLE_SEARCHER == null) {
					SET_GOOGLE_SEARCHER(true);
				} else {
					SET_GOOGLE_SEARCHER(false);
				}
			} else if ( evt.shiftKey ) {				
			} else {				
			}
		break;
		case 75:	// 'k'
			if (ctrl) {
				jMap.controller.insertHyperAction();
			}
			if (alt) {
				jMap.controller.insertImageAction();
			}			
		break;
		case 77:	// 'm'			
		break;
		case 78:	// 'n'
			if (ctrl) {
				location.href="/mindmap/new.do";
			}
		break;
		case 79:	// 'o'
			if (ctrl) {
				openMap();
			}
		break;
		case 80:	// 'p'
			if (ctrl) {
				if(ScaleAnimate.isShowMode())
					ScaleAnimate.endShowMode();
				else
					ScaleAnimate.startShowMode(30, 20, true);

			}
		break;
		case 81:	// 'q' 큐의 로그 보여주기
			if (ctrl) {
				window.open("/viewqueue.do?page="+location.pathname);
			}
		break;
		case 83:	// 's' save map
			if ( ctrl ) {
				if(!jMap.isSaved()) {
					saveMap();
				}
			} else if ( evt.shiftKey ) {				
			} else {				
			}
		break;
		case 88:	// 'x'
			if (ctrl) {
				jMap.controller.cutAction();
			}		
		break;
		case 86:	// 'v'
			if (ctrl) {
				jMap.controller.pasteAction();
			}				
		break;
		case 89:	// 'y'
			if (ctrl){
				jMap.controller.redoAction();
			}
		break;
		case 90:	// 'z'
			if (ctrl){
				jMap.controller.undoAction();
			}
		break;
		case 113:	// F2
			jMap.controller.editNodeAction();
		break;		
		case 13:	// ENTER
			jMap.controller.insertSiblingAction();
		break;
		case 27:	// ESC
			if ( ctrl ) {				
			} else if ( evt.shiftKey ) {				
			} else {				
			}
		break;
		case 32:	// SPACE
			jMap.controller.foldingAction();
		break;
		case 8:		// 'Mac' DELETE
		case 46:	// DELETE
			jMap.controller.deleteAction();
		break;
		case 9:		// TAB
		case 45:	// INSERT
			jMap.controller.insertAction();
		break;
		case 107:		// +
			jMap.scale(jMap.scaleTimes + 0.1);
		break;
		case 109:		// -
			jMap.scale(jMap.scaleTimes - 0.1);
		break;
	}

	// 이외 모든 키는 막습니다...
	return false;
}

JinoController.prototype.mousemove = function(e){
	var targ;
	if (!e) var e = window.event;
	if (e.target) targ = e.target;
	else if (e.srcElement) targ = e.srcElement;
	if (targ.nodeType == 3) // defeat Safari bug
		targ = targ.parentNode;
		
	if(targ.id == 'nodeEditor') return true;
	
	// Map 위치 이동
	if(jMap.DragPaper && jMap._enableDragPaper){
		var dx = e.clientX - DRAG_POS.x;
		var dy = e.clientY - DRAG_POS.y;
		DRAG_POS.x = e.clientX;
		DRAG_POS.y = e.clientY;
		
		this.work.scrollTop -= dy;		
		this.work.scrollLeft -= dx;		
	}

//	// Node 위치 이동	
//	if (jMap && jMap.movingNode) {
//		// 선택된 모든 노드를 움직인다
//		//for(var n=0; n < jMap.getSelecteds().length; n++){
//			//MoveWithChildNode(jMap.getSelecteds()[n], dx, dy);
//		//}
//		// 노드를 움직이기 위해 표시된 도형을 상대 좌표로 움직인다.
////		jMap.movingShape.translate(dx, dy);
//		
//		var targetNode = jMap.movingNode;
//		if(targetNode.isLeft()) dx = -dx;
//		targetNode.hgap = parseInt(targetNode.hgap) + dx;
//		targetNode.vshift = parseInt(targetNode.vshift) + dy;
//		
//		jMap.layoutManager.updateTreeHeightsAndRelativeYOfAncestors(targetNode);
//		jMap.layoutManager.layout(true);
//		
//		//var loc = targetNode.getLocation();
//		//MoveWithChildNode(jMap.getSelecteds().getLastElement(), dx, dy);
//    }
	
	return false;
}

JinoController.prototype.MoveWithChildNode = function(node, dx, dy){
    node.translate(dx, dy);	

	for (var i = node.children.length; i--;) {
		MoveWithChildNode(node.children[i], dx, dy);
    }
}

JinoController.prototype.mousedown = function(e){
	var targ;
	if (!e) var e = window.event;
	if (e.target) targ = e.target;
	else if (e.srcElement) targ = e.srcElement;
	if (targ.nodeType == 3) // defeat Safari bug
		targ = targ.parentNode;
	
	if(targ.id == 'nodeEditor') return true; 
	if(STAT_NODEEDIT) jMap.controller.stopNodeEdit(true);
	
	// 노드 이외의 공간을 찾아 내야 하는데...
	// 크롬, 사파리는 'jinomap'을 찾아야 된다.. 하지만 저건 스크롤바에 문제가..
	if (targ.id == 'paper_mapview') {
		DRAG_POS.x = e.clientX;
		DRAG_POS.y = e.clientY;
		
		jMap.DragPaper = true;
		
		// 배경에서의 context menu는 무시한다.
		//oOkmContextMenu && oOkmContextMenu.cancel();
		jMap.controller.blurAll();
	}
	// 크롬, 사파리...
	else if(targ.id == 'jinomap') {
		// clientX, clientY 값에 offsetLeft, offsetTop 값이 포함되어 있으므로 이를 고려해서 더해주어야 함
		if( targ.offsetLeft <= e.clientX && e.clientX < targ.clientWidth + targ.offsetLeft
		  && targ.offsetTop <= e.clientY && e.clientY < targ.clientHeight + targ.offsetTop) {
			DRAG_POS.x = e.clientX;
			DRAG_POS.y = e.clientY;
			
			jMap.DragPaper = true;
		}
		
		// 배경에서의 context menu는 무시한다.
		//oOkmContextMenu && oOkmContextMenu.cancel();
		jMap.controller.blurAll();
	}
}

JinoController.prototype.mouseup = function(e){
	e = e || window.event;	
	
	jMap.DragPaper = false;
	jMap.positionChangeNodes = false;
//	jMap.movingNode = false;
	
	//return false;
}


/**
 * 노드 편집시 키 이벤트
 * @param {Event} evt
 */
JinoController.prototype.nodeEditKeyDown = function(evt){
	evt = evt || window.event;

/*	
	// ctrl 및 meta 키
	var ctrl = null; 
    if (evt) ctrl = evt.ctrlKey; 
    else if (evt && document.getElementById) ctrl=(Event.META_MASK || Event.CTRL_MASK)
    else if (evt && document.layers) ctrl=(evt.metaKey || evt.ctrlKey);
    // alt 키
    var alt = evt.altKey
	
	var code = evt.keyCode;
*/
	
	if (F_CheckKey(evt,[27])) {					// esc 키
		if(J_NODE_CREATING){
			var node = null;	
			var parentNode = null;		
			while (node = jMap.getSelecteds().pop()) {
				parentNode = node.getParent();								
				node.remove();
			}
			J_NODE_CREATING.focus(true);
			
			jMap.layoutManager.updateTreeHeightsAndRelativeYOfAncestors(parentNode);
			jMap.layoutManager.layout(true);
		}
		jMap.controller.stopNodeEdit(false);
	} else if (F_CheckKey(evt,[13])) {			// enter 키
		if (BrowserDetect.browser == "Firefox" && jMap.keyEnterHit++ == 0) {
			return false;
		}
		if (evt.shiftKey) {
			// 높이 늘리기
			var oInput = jMap.controller.nodeEditor;		
			oInput.style.height = oInput.offsetHeight + 9 + "px";
			return true;			
		}		
		jMap.controller.stopNodeEdit(true);
			
		return false;
	}
	
	return true;
}

JinoController.prototype.setNodeEditor = function(el) {
	this.nodeEditor = el;
	if ( this.nodeEditor ) {
		this.nodeEditor.style.display = "none";
		this.nodeEditor.onkeypress = this.nodeEditKeyDown;
		//this.nodeEditor.onkeydown = this.nodeEditKeyDown;
	}
}

JinoController.prototype.startNodeEdit = function(node){
	if ( this.nodeEditor == undefined || this.nodeEditor == null || node.removed ) {
		return false;
	}
	
	var hGap = TEXT_HGAP;
	var vGap = TEXT_VGAP;
	
	if(STAT_NODEEDIT) this.stopNodeEdit(true);
	
	STAT_NODEEDIT = true;
	
	this.nodeEditor.setAttribute("nodeID", node.id);
	
	var oInput = this.nodeEditor;
	
	var viewBox = [];
	viewBox.x = 0;
	viewBox.y = 0;
	viewBox.width = RAPHAEL.getSize().width;
	viewBox.height = RAPHAEL.getSize().height;
	if(RAPHAEL.canvas.getAttribute("viewBox")){
		var vb = RAPHAEL.canvas.getAttribute("viewBox").split(" ");
		viewBox.x = vb[0];
		viewBox.y = vb[1];
		viewBox.width = vb[2];
		viewBox.height = vb[3];		
	}
	
	oInput.style.fontFamily = node.text.attr()['font-family'];
	oInput.style.fontSize = node.text.attr()['font-size'] * this.map.cfg.scale +"px";
	if(node.isLeft()) oInput.style.textAlign = "right";
	else oInput.style.textAlign = "left";	
	//oInput.style.fontStyle = ( node.font.italic == "true" )? "italic":"normal";
	//oInput.style.fontWeight = ( node.font.bold == "true" )? "bold":"normal";
	//oInput.style.color = ( node.color == "" )? "#"+NODE_FONT_COLOR:"#"+node.color;
	
	var width = node.body.getBBox().width * this.map.cfg.scale - hGap;
	// 편집창 넓이가 70이하일 때는 강제로 늘리기
	//if(width < 70) width = 70;
	var height = node.body.getBBox().height * this.map.cfg.scale - vGap;	
	var left = (node.body.getBBox().x - viewBox.x) * RAPHAEL.getSize().width / viewBox.width + hGap / 4;
	var top = (node.body.getBBox().y - viewBox.y) * RAPHAEL.getSize().height / viewBox.height + vGap / 4;
	
	oInput.style.display = "";
	oInput.style.width = width + "px";
	oInput.style.height = height + vGap + "px";
	oInput.style.left = left + "px";
	oInput.style.top = top - vGap/4 + "px";
	oInput.style.zIndex = 999;	
	
	oInput.style.isleft = node.isLeft();
	oInput.value = node.getText();
	oInput.focus();
	/*
	if ( org == CARET_ORG_START ) {
		this.setCaretPos(oInput, 0);
	} else if ( org == CARET_ORG_END ) {
		this.setCaretPos(oInput, node.text.length);
	} else {
		oInput.select();
	}
	
	this.panelD.onmousedown = null;
	*/
	return true;
	
}

// res - true: 편집 적용, false: 편집 취소
JinoController.prototype.stopNodeEdit = function(res) {
	STAT_NODEEDIT = false;
	J_NODE_CREATING = false;
	
	jMap.work.focus();
	
	if ( this.nodeEditor == undefined || this.nodeEditor == null ) {
		return null;
	}
	
	if ( res == false ) {
		this.nodeEditor.style.display = "none";
		return null;
	}
	
	var nodeID = this.nodeEditor.getAttribute("nodeID");
	if ( nodeID == undefined || nodeID == null || nodeID == "") {
		this.nodeEditor.style.display = "none";
		return null;
	}
	
	var node = this.map.getNodeById(nodeID);
	if ( node == undefined || node == null ) {
		this.nodeEditor.style.display = "none";
		return null;
	}
	
	this.nodeEditor.style.display = "none";
	this.nodeEditor.setAttribute("nodeID", "");
	
	var oInput = this.nodeEditor;
	
	var val = JinoUtil.trimStr(oInput.value);
	if ( val == node.getText() ) return null;
	
	node.setText(val);
	
	jMap.layoutManager.updateTreeHeightsAndRelativeYOfAncestors(node);
	jMap.layoutManager.layout(true);
		
	return node;
}

JinoController.prototype.blurAll = function(){
	var selectedNodes = jMap.getSelecteds();
	for(var i = selectedNodes.length-1; i >= 0; i--) {
		selectedNodes[i].blur();
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// ACTIONS /////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////


JinoController.prototype.copyAction = function(){
	jMap.clipboardManager.toClipboard(jMap.getSelecteds(), true);
}

JinoController.prototype.cutAction = function(selectedNodes){
	if(!selectedNodes) selectedNodes = jMap.getSelecteds();
	
	jMap.clipboardManager.toClipboard(selectedNodes);
	
	for (var i = 0; i < selectedNodes.length; i++) {		
		selectedNodes[i].remove();
	}
	
	var parentNode = selectedNodes[0].parent;
	jMap.layoutManager.updateTreeHeightsAndRelativeYOfAncestors(parentNode);
	jMap.layoutManager.layout(true);
	
	return parentNode;
}

JinoController.prototype.pasteAction = function(selected){
	if(!selected) selected = jMap.getSelected();
	
	// 노드를 펼친다.
	selected.folded && selected.setFolding(false);
	
	// 선택한 노드에 클립보드에 있는 노드들을 붙인다.
	var pasteNodes = jMap.loadManager.pasteNode(selected, jMap.clipboardManager.getClipboardText());
	
	// 붙여넣기한 노드를 저장
	// 붙여넣기는 이미 데이터가 있기 때문에 저장후에 화면에 렌더링 할 수 있지만
	// 붙여넣기 하는 과정중에 POSITION 속성은 새로 만들어 지기 때문에 (다른 속성도?)
	// 렌더링된 후에 저장하는 것이다.
	for (var i = 0; i < pasteNodes.length; i++) {
		jMap.saveAction.pasteAction(pasteNodes[i]);
	}
	
	// 레이지 로딩일 경우, 자식들을 모두 삭제 한다.
	// 위에서 이미 서버에 저장되어 있고,
	// 붙여넣은 노드의 로딩은 모두 레이지로딩으로 한다.
	if(jMap.cfg.lazyLoading) {
		for (var i = 0; i < pasteNodes.length; i++) {
			var children = pasteNodes[i].getChildren();
			for (var c = children.length-1; c >= 0; c--) {
				children[c].removeExecute();				
			}
		}
	}
	
	// 이벤트 리스너를 위한 데이터
	// copy&paste의 경우 노드 아이디가 다시 만들어지기 때문에
	var sendXml = "<clipboard>";	
	for(var i = 0; i < pasteNodes.length; i++) {
			var xml = pasteNodes[i].toXML();
			sendXml += xml;
	}	
	sendXml += "</clipboard>";
	
	// 이벤트 리스너 호출
	jMap.fireActionListener(ACTIONS.ACTION_NODE_PASTE, selected, sendXml);
	
	jMap.initFolding(selected);
	jMap.layoutManager.updateTreeHeightsAndRelativeYOfDescendantsAndAncestors(selected);
	jMap.layoutManager.layout(true);
}

JinoController.prototype.deleteAction = function(){
	var node = null;
	var parentNode = null;
	var indexPos = -1;
	while (node = jMap.getSelecteds().pop()) {
		parentNode = node.getParent();
		indexPos = node.getIndexPos();
		node.remove();
	}
	
	if (parentNode) {
		jMap.layoutManager.updateTreeHeightsAndRelativeYOfAncestors(parentNode);
		jMap.layoutManager.layout(true);
	}
	
	// 노드를 삭제후 적정한 노드위치로 포커싱
	if (indexPos != -1) {
		if (parentNode.getChildren().length <= 0) {
			parentNode.focus();
		} else {
			if (parentNode.getChildren().length > indexPos) {
				parentNode.getChildren()[indexPos].focus();
			} else {
				parentNode.getChildren()[parentNode.getChildren().length - 1].focus();
			}
		}
		
	}
}

JinoController.prototype.editNodeAction = function(){
	jMap.getSelecteds().getLastElement() &&
		jMap.controller.startNodeEdit(jMap.getSelecteds().getLastElement());
}

JinoController.prototype.insertAction = function(){
	var node = jMap.getSelecteds().getLastElement();
	if (node) {
		J_NODE_CREATING = node;
		node.folded && node.setFolding(false);
		var newNode = jMap.createNodeWithCtrl(node);
		newNode.focus(true);
		
		// 부모 노드를 넘겨야 되나 자신을 넘겨야 제대로 됨..
		// 문제가 되지 않으면 이데로 놔두자...
		jMap.layoutManager.updateTreeHeightsAndRelativeYOfAncestors(node);
		jMap.layoutManager.layout(true);
		
		newNode.setTextExecute("");
		jMap.controller.startNodeEdit(newNode);
	}
}

JinoController.prototype.insertSiblingAction = function(){
	// FireFox의 엔터키 버그?? 다른 이벤트에서 기대하지 않은 이벤트 발생...
	if (BrowserDetect.browser == "Firefox") {
		jMap.keyEnterHit = 0;
	}
	
	var selectedNode = jMap.getSelecteds().getLastElement();
	var node = selectedNode && selectedNode.parent;
	if (node) {
		J_NODE_CREATING = selectedNode;
		// 폴딩 필요할까? 필요없음.
//		node.folded && node.setFolding(false);
		var index = selectedNode.getIndexPos() + 1;
		var position = null;
		// Root노드 자식에서 추가될 경우 왼쪽 오른쪽 고려
		if (selectedNode.position && selectedNode.getParent().isRootNode()) 
			position = selectedNode.position;
		var newNode = jMap.createNodeWithCtrl(node, null, null, index, position);		
		newNode.focus(true);
		
		jMap.layoutManager.updateTreeHeightsAndRelativeYOfAncestors(newNode);
		jMap.layoutManager.layout(true);
		
		newNode.setTextExecute("");
		jMap.controller.startNodeEdit(newNode);
	}
}

JinoController.prototype.insertHyperAction = function(){
	var selected = jMap.getSelecteds().getLastElement();
	
	var urlText = selected.hyperlink && selected.hyperlink.attr().href;
	urlText = urlText || "http://";
	var txt = '<center>Insert HyperLink</center><br />URL:<br />' +
	'<input type="text" id="jino_input_url"' +
	'name="jino_input_url" value=' +
	urlText +
	' />';
	function callbackform_hyper(v, m, f){
		if (v) {
			selected.setHyperlink(f.jino_input_url);
			jMap.layoutManager.updateTreeHeightsAndRelativeYOfAncestors(selected);
			jMap.layoutManager.layout(true);
		}
		jMap.work.focus();
	}
	var re = $.prompt(txt, {
		callback: callbackform_hyper,
		persistent: false,
		focusTarget: 'jino_input_url',
		top: '30%',
		buttons: {
			Ok: true
		}
	});
}

JinoController.prototype.insertImageAction = function(){
	var selected = jMap.getSelecteds().getLastElement();
	
	var urlImg = selected.img && selected.img.attr().src;
	urlImg = urlImg || "http://";
	var txt = '<center>Insert Image</center><br />URL:<br />' +
	'<input type="text" id="jino_input_img_url"' +
	'name="jino_input_img_url" value=' +
	urlImg +
	' />';
	function callbackform_img(v, m, f){
		if (v) {
			selected.setImage(f.jino_input_img_url);
			jMap.layoutManager.updateTreeHeightsAndRelativeYOfAncestors(selected);
			jMap.layoutManager.layout(true);
		}
		jMap.work.focus();
	}
	var re = $.prompt(txt, {
		callback: callbackform_img,
		persistent: false,
		focusTarget: 'jino_input_img_url',
		top: '30%',
		buttons: {
			Ok: true
		}
	});
}

JinoController.prototype.findNodeAction = function(){
	var txt = 'Find : ' +
	'<input type="text" id="jino_input_search_text"' +
	'name="jino_input_search_text" value="" />' +
	'<br /><br />' +
	'<input type="checkbox" id="jino_check_search_ignorecase"' +
	'name="jino_check_search_ignorecase" value="" checked>ignorecase' +
	'<input type="checkbox" id="jino_check_search_wholeword"' +
	'name="jino_check_search_wholeword" value="">wholeword';
	function callbackform_search(v, m, f){
		if (v) {
			var searchText = f.jino_input_search_text;
			var isIgnorecase = f.jino_check_search_ignorecase;
			var isWholeword = f.jino_check_search_wholeword;
			var nodes = jMap.findNode(searchText, isWholeword, isIgnorecase, jMap.getSelecteds().getLastElement());
			for (var i = 0; i < nodes.length; i++) {
				var node = nodes[i].node;
				
				var currentNode = node;
				while (!currentNode.isRootNode()) {
					currentNode = currentNode.getParent();
					currentNode.folded && currentNode.setFoldingExecute(false);
				}
				
				node.focus(false);
			}
			jMap.layoutManager.updateTreeHeightsAndRelativeYOfWholeMap();
			jMap.layoutManager.layout(true);
		}
		jMap.work.focus();
	}
	var re = $.prompt(txt, {
		callback: callbackform_search,
		persistent: false,
		focusTarget: 'jino_input_search_text',
		top: '30%',
		buttons: {
			Find: true
		}
	});
}

JinoController.prototype.foldingAction = function(node){
	if(!node) node = jMap.getSelecteds().getLastElement();
	// Folding & unFolding
	node.setFolding(!node.folded);
	jMap.layoutManager.updateTreeHeightsAndRelativeYOfDescendantsAndAncestors(node);
	jMap.layoutManager.layout(true);
}

JinoController.prototype.foldingAllAction = function(){
	var selected = jMap.getSelecteds().getLastElement();
	selected.setFoldingAll(true);
	jMap.layoutManager.updateTreeHeightsAndRelativeYOfWholeMap();
	jMap.layoutManager.layout(true);
}

JinoController.prototype.unfoldingAllAction = function(){
	var selected = jMap.getSelecteds().getLastElement();
	selected.setFoldingAll(false);
	jMap.layoutManager.updateTreeHeightsAndRelativeYOfWholeMap();
	jMap.layoutManager.layout(true);
}
	
JinoController.prototype.parentNodeFocusAction = function(selected){
	selected.getParent().focus(true);
}

JinoController.prototype.childNodeFocusAction = function(selected){
	if(selected.folded) {
		selected.setFolding(false);
		jMap.layoutManager.updateTreeHeightsAndRelativeYOfDescendantsAndAncestors(selected);				
		jMap.layoutManager.layout(true);
		
		return false;
	}
	var children = selected.getChildren();
	if(children.length > 0){
		children[0].focus(true);
	}
}

JinoController.prototype.prevSiblingNodeFocusAction = function(selected){
	var prevSiblingNode = selected.prevSibling(true);
	if(prevSiblingNode){
		var parentNode = prevSiblingNode.getParent();
		// 자신의 부모는 항상 언폴딩 되어 있지 않은가?? 삭제해도 무관하지만..
		if(parentNode.folded) {
			parentNode.setFolding(false);
			jMap.layoutManager.updateTreeHeightsAndRelativeYOfDescendantsAndAncestors(parentNode);				
			jMap.layoutManager.layout(true);
		}				
		prevSiblingNode.focus(true);
	}
}

JinoController.prototype.nextSiblingNodefocusAction = function(selected){
	var nextSiblingNode = selected.nextSibling(true);
	if(nextSiblingNode){
		var parentNode = nextSiblingNode.getParent();
		// 자신의 부모는 항상 언폴딩 되어 있지 않은가?? 삭제해도 무관하지만..
		if(parentNode.folded) {
			parentNode.setFolding(false);
			jMap.layoutManager.updateTreeHeightsAndRelativeYOfDescendantsAndAncestors(parentNode);				
			jMap.layoutManager.layout(true);
		}				
		nextSiblingNode.focus(true);
	}
}

JinoController.prototype.nodeStructureFromText = function(node){
	if(!node) node = jMap.getSelecteds().getLastElement();
	
	var txt = '다음 텍스트를 노드로 생성합니다.<br/><center><textarea id="okm_node_structure_textarea" name="okm_node_structure_textarea" ' +
				'onkeydown="return $.prompt.interceptTabs(event, this);" cols="65" rows="10">' +
				'</textarea></center>';
	function callbackform_structure(v, m, f){
		if (v) {
			var stText = f.okm_node_structure_textarea;
			
			node.folded && node.setFoldingExecute(false);
			jMap.createNodeFromText(node, stText);
			jMap.layoutManager.updateTreeHeightsAndRelativeYOfDescendantsAndAncestors(node);
			jMap.layoutManager.layout(true);
		}
		jMap.work.focus();
	}
	var re = $.prompt(txt, {
		callback: callbackform_structure,
		persistent: false,
		focusTarget: 'okm_node_structure_textarea',
		top: '30%',
		enterToOk: false,
		buttons: {
			Insert: true
		}
	});
}

JinoController.prototype.nodeStructureToText = function(node){
	if(!node) node = jMap.getSelecteds().getLastElement();
	
	var text = jMap.createTextFromNode(node, "\t");
	
	var txt = '다음은 노드의 구조입니다.<br/><center><textarea id="okm_node_structure_textarea" name="okm_node_structure_textarea" ' +
				'onkeydown="return $.prompt.interceptTabs(event, this);" cols="65" rows="10">' +
				text + 
				'</textarea></center>';
	var re = $.prompt(txt, {
		persistent: false,
		focusTarget: 'okm_node_structure_textarea',
		top: '30%',
		buttons: {
			Close: true
		}
	});
}

JinoController.prototype.nodeStructureFromXml = function(node){
	if(!node) node = jMap.getSelecteds().getLastElement();
	
	var txt = '다음 XML을 노드로 생성합니다.<br/><center><textarea id="okm_node_structure_textarea" name="okm_node_structure_textarea" ' +
				'onkeydown="return $.prompt.interceptTabs(event, this);" cols="65" rows="10">' +
				'</textarea></center>';
	function callbackform_structure(v, m, f){
		if (v) {
			var xmlStr = f.okm_node_structure_textarea;
			
			var pasteNodes = jMap.loadManager.pasteNode(node, xmlStr);
			
			// 저장
			for (var i = 0; i < pasteNodes.length; i++) {
				jMap.saveAction.pasteAction(pasteNodes[i]);
			}
			
			// 이벤트 리스너 호출
			jMap.fireActionListener(ACTIONS.ACTION_NODE_PASTE, node, xmlStr);
			
			jMap.initFolding(node);
			jMap.layoutManager.updateTreeHeightsAndRelativeYOfDescendantsAndAncestors(node);
			jMap.layoutManager.layout(true);
		}
		jMap.work.focus();
	}
	var re = $.prompt(txt, {
		callback: callbackform_structure,
		persistent: false,
		focusTarget: 'okm_node_structure_textarea',
		top: '30%',
		enterToOk: false,
		buttons: {
			Insert: true
		}
	});
}

JinoController.prototype.nodeStructureToXml = function(node){
	if(!node) node = jMap.getSelecteds().getLastElement();
	
	var text = "<okm>" + node.toXML() + "</okm>";
	
	var txt = '다음은 노드의 구조입니다.<br/><center><textarea id="okm_node_structure_textarea" name="okm_node_structure_textarea" ' +
				'onkeydown="return $.prompt.interceptTabs(event, this);" cols="65" rows="10">' +
				text + 
				'</textarea></center>';
	var re = $.prompt(txt, {
		persistent: false,
		focusTarget: 'okm_node_structure_textarea',
		selectText: 'okm_node_structure_textarea',
		top: '30%',		
		buttons: {
			Close: true
		}
	});
}

JinoController.prototype.nodeTextColorAction = function(node){
	if(!node) node = jMap.getSelecteds().getLastElement();
	
	var txt = '<center>Text Color</center><br />' +
	'<div style="height:130px;">' + 
	'<input type="text" style="width:220px;" class="color" id="jino_input_textcolor"' +
	'name="jino_input_textcolor" />' +
	'</div>' + 
	'<script type="text/javascript">' +
	'var myPicker = new jscolor.color(document.getElementById("jino_input_textcolor"), {});' +
	'myPicker.hash = true;' +
	'myPicker.pickerFaceColor = "transparent";' +
	'myPicker.pickerFace = 5;' +
	'myPicker.pickerBorder = 0;' +
	'myPicker.pickerInsetColor = "black";' +
	//'myPicker.showPicker();' +
	'myPicker.fromString("' + node.getTextColor() + '");' +
	//'myPicker.pickerPosition = "top"' +
	'</script>';
	
	function callbackform_color(v, m, f){
		if (v) {
			node.setTextColor(f.jino_input_textcolor);
		}
		jMap.work.focus();
	}
	var re = $.prompt(txt, {
		callback: callbackform_color,
		persistent: true,
		focusTarget: 'jino_input_textcolor',
		prefix:'jqicolor',
		top: '30%',
		buttons: {
			Ok: true
		}
	});
}

JinoController.prototype.nodeBackgroundColorAction = function(node){
	if(!node) node = jMap.getSelecteds().getLastElement();
	
	var txt = '<center>Background Color</center><br />' +
	'<div style="height:130px;">' + 
	'<input type="text" style="width:220px;" class="color" id="jino_input_bgcolor"' +
	'name="jino_input_bgcolor" />' +
	'</div>' + 
	'<script type="text/javascript">' +
	'var myPicker = new jscolor.color(document.getElementById("jino_input_bgcolor"), {});' +
	'myPicker.hash = true;' +
	'myPicker.pickerFaceColor = "transparent";' +
	'myPicker.pickerFace = 5;' +
	'myPicker.pickerBorder = 0;' +
	'myPicker.pickerInsetColor = "black";' +
	//'myPicker.showPicker();' +
	'myPicker.fromString("' + node.getBackgroundColor() + '");' +
	//'myPicker.pickerPosition = "top"' +
	'</script>';
	
	function callbackform_color(v, m, f){
		if (v) {
			node.setBackgroundColor(f.jino_input_bgcolor);
		}
		jMap.work.focus();
	}
	var re = $.prompt(txt, {
		callback: callbackform_color,
		persistent: true,
		focusTarget: 'jino_input_bgcolor',
		prefix:'jqicolor',
		top: '30%',
		buttons: {
			Ok: true
		}
	});
}

JinoController.prototype.deleteArrowlinkAction = function(node){
	if(!node) node = jMap.getSelecteds().getLastElement();

	for (var i = 0; i < node.arrowlinks.length; i++) {		
		node.removeArrowLink(node.arrowlinks[i]);
	}
}

JinoController.prototype.screenFocusAction = function(node){
	if(!node) node = jMap.getSelecteds().getLastElement();
	if(!node) node = jMap.getRootNode();
	node.screenFocus();
}

JinoController.prototype.undoAction = function(){
	jMap.historyManager && jMap.historyManager.undo();
}

JinoController.prototype.redoAction = function(){
	jMap.historyManager && jMap.historyManager.redo();
}


//////////////////////////////////////////ACTIONS /////////////////////////////////////////////////////

