/**
 * 글꼴 크기를 변경하는 역활을 합니다.
 * 
 * sContainer : 선택된 문자열 범위 중에 첫번째 노드 (IE) 자료형 Object
 * sOffset : sContainer에서 range의 시작 지점 (IE) 자료형 number
 * eContainer : 선택된 문자열 범위 중에 마지막 노드 (IE) 자료형 Object
 * eOffset : eContainer에서 range가 끝나는 지점 (IE) 자료형 number
 * startFlag : sContainer를 찾았는지의 여부를 나타냄 (IE) 자료형 boolean
 * endFlag : eContainer를 찾았는지의 여부를 나타냄 (IE) 자료형 boolean
 * sCommand : 실행할 커맨드 (IE) 자료형 String
 * 
 * @param command : 실행할 커맨드
 * @param value : 글꼴 크기 값 또는 글꼴 종류
 */
EditorManager.prototype.font = function(command, value) {
	if(EditorUtil.agent().IE){
		this.sContainer = null;
		this.sOffset = 0;
		this.eContainer = null;
		this.eOffset = 0;
		this.startFlag = false;
		this.endFlag = false;
		this.sCommand = command;	
		if(this.oRange.text.length == 0){
			this.oRange.pasteHTML(this.sCommand == 'fontSize'?'<span id="asnet" style="FONT-SIZE: '+value+'pt;">&nbsp;</span>':'<span id="asnet" style="FONT-FAMILY: '+value+';">&nbsp;</span>');
			this.oRange.moveToElementText(this.oDoc.getElementById('asnet'));
			this.oDoc.getElementById('asnet').removeAttribute('id');
			this.oRange.select();
		}else{
			this.getsOffset();
			this.geteOffset();
			this.IEsplitNode(this.sContainer, this.eContainer, this.sOffset, this.eOffset);
			this.getParentElement(this.oRange.parentElement());
			if(this.startFlag && this.endFlag){
				this.startFlag = false;
				this.endFlag = false;
				this.roop(this.oRange.parentElement(), value);
			}else{
				this.startFlag = false;
				this.endFlag = false;
				this.roop(this.oRange.parentElement().parentElement, value);
			}
		}		
	}else{
		this.collapsed = this.oRange.collapsed;
		this.getSelection();
		this.splitTextNode();
		
		if(this.collapsed){
        	oSpan = this.oDoc.createElement("SPAN");
        	if(command.toLowerCase()=="fontname") oSpan.style.fontFamily =  value;
        	else if(command.toLowerCase()=="fontsize") oSpan.style.fontSize =  value + "pt";
    		this.oRange.deleteContents();
    		this.oRange.insertNode(oSpan);
    		this.oRange.setStart(oSpan,0);
			return;	
		}
		
		var oCommonAncestorContainer = this.oRange.commonAncestorContainer;	
		var oAllNodes = this.getAllNodes();

	    var oNode;
	    for(var i=0; i<oAllNodes.length; i++){
	    	oNode = oAllNodes[i];
	        if(!oNode)continue;
	        if(oNode.nodeType!=3)continue;
	        if(oNode.nodeValue=="")continue;
	        this.log(oNode.nodeType);
	        
	        if(oNode.parentNode.tagName.toUpperCase()=="SPAN" && oNode.parentNode.textContent==oNode.nodeValue){
	        	if(command.toLowerCase()=="fontname") oNode.parentNode.style.fontFamily = value;
	        	else if(command.toLowerCase()=="fontsize") oNode.parentNode.style.fontSize = value + "pt";	        		
	        }else{
	        	oSpan = this.oDoc.createElement("SPAN");
	        	if(command.toLowerCase()=="fontname") oSpan.style.fontFamily =  value;
	        	else if(command.toLowerCase()=="fontsize") oSpan.style.fontSize =  value + "pt";	        	
	        	oNode.parentNode.insertBefore(oSpan, oNode);
	        	oSpan.appendChild(oNode);
	        }
	    }		
	}
}
EditorManager.prototype.getRangeProperties = function(){
	var rangeProperties = {
		oStartContainer : this.oRange.startContainer,
		startOffset : this.oRange.startOffset,
		oEndContainer : this.oRange.endContainer,
		endOffset : this.oRange.endOffset
	}
	this.oStartContainer = rangeProperties.oStartContainer;
	this.startOffset = rangeProperties.startOffset;
	this.oEndContainer = rangeProperties.oEndContainer;
	this.endOffset = rangeProperties.endOffset;	
}
/**
 * EditorManager.splitTextNode() 는 TextNode를 분리한 후 변경된 Container와 offset 값을 구합니다.
 */
EditorManager.prototype.splitTextNode = function(){
	var rangeProperties = {
		oStartContainer : this.oRange.startContainer,
		startOffset : this.oRange.startOffset,
		oEndContainer : this.oRange.endContainer,
		endOffset : this.oRange.endOffset
	}
	rangeProperties = this.splitStartTextNode(rangeProperties);
	rangeProperties = this.splitEndTextNode(rangeProperties);
	this.oStartContainer = rangeProperties.oStartContainer;
	this.startOffset = rangeProperties.startOffset;
	this.oEndContainer = rangeProperties.oEndContainer;
	this.endOffset = rangeProperties.endOffset;	
}
/**
 * EditorManager.splitStartTextNode() 는 startContainer에서 startOffset을 기준으로 TextNode를 분리합니다.
 * 
 * @param rangeProperties
 * @return 변경된 rangeProperties 값을 반환합니다.
 */
EditorManager.prototype.splitStartTextNode = function(rangeProperties){
    var oStartContainer=rangeProperties.oStartContainer;
    var startOffset=rangeProperties.startOffset;
    var oEndContainer=rangeProperties.oEndContainer;
    var endOffset=rangeProperties.endOffset;
    
    if(!oStartContainer)return rangeProperties;
    if(oStartContainer.nodeType!=3)return rangeProperties;
    if(startOffset==0)return rangeProperties;
    if(oStartContainer.nodeValue.length<=startOffset)return rangeProperties;
    var oLastPart=oStartContainer.splitText(startOffset);
    if(oStartContainer==oEndContainer){
    	endOffset-=startOffset;
        oEndContainer=oLastPart;
    }
    oStartContainer=oLastPart;
    startOffset=0;
    return{
        oStartContainer:oStartContainer,
        startOffset:startOffset,
        oEndContainer:oEndContainer,
        endOffset:endOffset
    };
}
/**
 * EditorManager.splitEndTextNode() 는 endContainer에서 endOffset을 기준으로 TextNode를 분리합니다.
 * 
 * @param rangeProperties
 * @return 변경된 rangeProperties 값을 반환합니다.
 */
EditorManager.prototype.splitEndTextNode = function(rangeProperties){
    var oStartContainer=rangeProperties.oStartContainer;
    var startOffset=rangeProperties.startOffset;
    var oEndContainer=rangeProperties.oEndContainer;
    var endOffset=rangeProperties.endOffset;
    if(!oEndContainer)return rangeProperties;
    if(oEndContainer.nodeType!=3)return rangeProperties;
    if(endOffset>=oEndContainer.nodeValue.length)return rangeProperties;
    if(endOffset==0)return rangeProperties;
    oEndContainer.splitText(endOffset);
    return{
        oStartContainer:oStartContainer,
        startOffset:startOffset,
        oEndContainer:oEndContainer,
        endOffset:endOffset
    };	
}
/**
 * EditorManager.getAllNodes() 는 Range영역의 모든 NODE를 구하여 반환합니다.
 * 
 * @return
 */
EditorManager.prototype.getAllNodes = function(){
    if(this.collapsed)return[];
    var oStartNode = this.getStartNode();
    this.log("oStartNode : " + oStartNode.nodeValue + ", "+oStartNode.nodeType);
    
    var oEndNode = this.getEndNode();
    this.log("oEndNode : " + oEndNode.nodeValue + ", "+oEndNode.nodeType);
    return this.getNodeBetween(oStartNode,oEndNode);	
}
/**
 * EditorManager.getStartNode() 는 startContainer에서 startNode를 구하여 반환합니다.
 * 
 * @return startNode를 반환합니다.
 */
EditorManager.prototype.getStartNode = function(){
    if(this.collapsed){
        if(this.oStartContainer.nodeType==3){
            if(this.startOffset==0)return null;
            if(this.oStartContainer.nodeValue.length<=this.startOffset)return null;
            return this.oStartContainer;
        }
        return null;
    }
    if(this.oStartContainer.nodeType==3){
        if(this.startOffset>=this.oStartContainer.nodeValue.length)return this.getNextNode(this.oStartContainer);
        return this.oStartContainer;
    }else{
        if(this.startOffset>=this.oStartContainer.childNodes.length)return this.getNextNode(this.oStartContainer);
        return this.oStartContainer.childNodes[this.startOffset];
    }
}
/**
 * EditorManager.getEndNode() 는 endContainer에서 endNode를 구하여 반환합니다.
 * 
 * @return endNode를 반환합니다.
 */
EditorManager.prototype.getEndNode = function(){
    if(this.collapsed)return this.getStartNode();
    if(this.oEndContainer.nodeType==3){
        if(this.endOffset==0)return this.getPrevNode(this.oEndContainer);
        return this.oEndContainer;
    }else{
        if(this.endOffset==0)return this.getPrevNode(this.oEndContainer);
        return this.oEndContainer.childNodes[this.endOffset-1];
    }
}
/**
 * EditorManager.getNodeBetween() 는 startNode와 endNode 사이의 모든 노드를 구합니다.
 * 
 * @param oStartNode
 * @param oEndNode
 * @return
 */
EditorManager.prototype.getNodeBetween = function(oStartNode,oEndNode){
	this.tempNumber = 0;
    var aNodesBetween=[];
    if(!oStartNode||!oEndNode)return aNodesBetween;
    this.getNextNodesUntil(oStartNode,oEndNode,aNodesBetween);
    return aNodesBetween;	
}
/**
 * EditorManager.getNextNodesUntil()
 * 
 * @param oStartNode
 * @param oEndNode
 * @param aNodesBetween
 * @return
 */
EditorManager.prototype.getNextNodesUntil = function(oStartNode,oEndNode,aNodesBetween){
    if(!oStartNode)return false;
    if(!this.getChildNodesUntil(oStartNode,oEndNode,aNodesBetween))return false;
    var oNextToChk=oStartNode.nextSibling;
    while(!oNextToChk){
        if(!oStartNode.parentNode)return false;
        oStartNode=oStartNode.parentNode;
        aNodesBetween[aNodesBetween.length]=oStartNode;
        if(oStartNode==oEndNode)return false;
        oNextToChk=oStartNode.nextSibling;
    }
    return this.getNextNodesUntil(oNextToChk,oEndNode,aNodesBetween);	
}
/**
 * EditorManager.getChildNodesUntil()
 * 
 * @param oStartNode
 * @param oEndNode
 * @param aNodesBetween
 * @return
 */
EditorManager.prototype.getChildNodesUntil = function(oStartNode,oEndNode,aNodesBetween){
    if(!oStartNode)return false;
    var bEndFound=false;
    var oCurNode=oStartNode;
    if(oCurNode.firstChild){
        oCurNode=oCurNode.firstChild;
        while(oCurNode){
            if(!this.getChildNodesUntil(oCurNode,oEndNode,aNodesBetween)){
                bEndFound=true;break; 
            }
            oCurNode=oCurNode.nextSibling;
        }
    }
    aNodesBetween[aNodesBetween.length]=oStartNode;
    if(bEndFound)return false;
    if(oStartNode==oEndNode)return false;
    return true;	
}
/**
 * EditorManager.getPrevNode() 는 가장 앞의 NODE를 찾아 반환합니다.
 * 
 * @param oNode
 * @return
 */
EditorManager.prototype.getPrevNode = function(oNode){
    if(!oNode||oNode.tagName=="BODY")return this.oDoc.body;
    if(oNode.previousSibling)return oNode.previousSibling;
    return this.getPrevNode(oNode.parentNode);
}
/**
 * EditorManager.getNextNode() 는 가장 뒤의 NODE를 찾아 반환합니다.
 * 
 * @param oNode
 * @return
 */
EditorManager.prototype.getNextNode = function(oNode){
    if(!oNode||oNode.tagName=="BODY")return this.oDoc.body;
    if(oNode.nextSibling)return oNode.nextSibling;
    return this.getNextNode(oNode.parentNode);
}
/**
 * EditorManager.roop() 는(은) startNode와 endNode 사이에 있는 textNode들을 찾는 역활을 합니다. 
 * IE 전용
 * @param node : startNode와 endNode를 포함하고 있는 노드 - object
 * @param value : 글꼴 크기 값 또는 글꼴 종류 - String
 */
EditorManager.prototype.roop = function(node, value) {
	var childes = node.childNodes;
	if(this.sContainer == this.eContainer){
		this.start_End_IsEqual(value);
		return;
	}
	for(var i=0; i<childes.length; i++){
		if(childes[i] == this.sContainer){
			this.pasteSpan_sContainer(value);	
		}else if(childes[i] == this.eContainer){
			this.pasteSpan_eContainer(value);	
		}else if(childes[i].nodeType == 3){
			if(childes[i].nodeValue.length == 0)continue;
			this.pasteSpan(value, childes[i]);	
		}else if(childes[i].nodeType == 1){	
			if(childes[i].childNodes){	
				this.roop(childes[i], value);	 
			}
		}
		if(this.endFlag){	
			break;
		}
	}
}
/**
 * EditorManager.start_End_IsEqual() 는(은) startNode 와 endNode가 같은 경우에 처리 해주는 역활을 합니다.
 * IE 전용
 * @param value : 글꼴 크기 값 또는 글꼴 종류 - String
 */
EditorManager.prototype.start_End_IsEqual = function(value){
	if(this.sContainer.parentNode.nodeName == 'SPAN') if(this.style_Change(this.sContainer.parentNode, value)) return;
	var newElement = this.oDoc.createElement('SPAN');
	this.sCommand == 'fontSize'?newElement.style.fontSize = value+'pt':newElement.style.fontFamily = value;
	newElement.innerText = this.sContainer.nodeValue;
	this.sContainer.parentNode.replaceChild(newElement,this.sContainer);
}
/**
 * 선택된 문자열 범위 중에 첫번째 노드에 대한 처리를 합니다.
 * IE 전용
 * @param value : 글꼴 크기 값 또는 글꼴 종류 - String
 */
EditorManager.prototype.pasteSpan_sContainer = function(value){
	this.startFlag = true;	
	if(this.sContainer.parentNode.nodeName == 'SPAN'){ 
		if(this.style_Change(this.sContainer.parentNode, value)) return;
	}
	if(this.sContainer.nodeType == 3){
		var newElement = this.oDoc.createElement('SPAN');
		this.sCommand == 'fontSize'?newElement.style.fontSize = value+'pt':newElement.style.fontFamily = value;
		newElement.innerText = this.sContainer.nodeValue;
		this.sContainer.parentNode.insertBefore(newElement,this.sContainer);
		this.sContainer.parentNode.removeChild(this.sContainer);
	}else if(this.sContainer.nodeType == 1){
		this.roop(this.sContainer, value);
	}
}
/**
 * 선택된 문자열 범위 중에 첫번째 노드와 마지막 노드를 제외 한 나머지 노드들을 처리 합니다.
 * IE 전용
 * @param value : 글꼴 크기 값 또는 글꼴 종류 - String
 * @param node : 처리 할 노드 - object
 */
EditorManager.prototype.pasteSpan = function(value, node){
	if(!this.startFlag)	return;	
	if(node.parentNode.nodeName == 'SPAN') if(this.style_Change(node.parentNode, value)) return;
	var newNode = this.oDoc.createElement('SPAN');
	this.sCommand == 'fontSize'?newNode.style.fontSize = value+'pt':newNode.style.fontFamily = value;
	newNode.innerText = node.nodeValue;
	node.parentNode.replaceChild(newNode,node);
	
}
/**
 * 선택된 문자열 범위 중에 끝에 있는 node에 대한 처리를 합니다.
 * IE 전용
 * @param value : 글꼴 크기 값 또는 글꼴 종류 - String
 */
EditorManager.prototype.pasteSpan_eContainer = function(value){
	
	if(this.eContainer.parentNode.nodeName == 'SPAN'){
		if(this.style_Change(this.eContainer.parentNode, value)){
			this.endFlag = true;	
			this.startFlag = false;	
			return;
		}
	}
	if(this.eContainer.nodeType == 3){
		var newElement = this.oDoc.createElement('SPAN');
		this.sCommand == 'fontSize'?newElement.style.fontSize = value+'pt':newElement.style.fontFamil = value;
		newElement.innerText = this.eContainer.nodeValue;
		this.eContainer.parentNode.insertBefore(newElement, this.eContainer);
		/**
		 *replaceChild method를 사용 하면 range가 빠지기 때문에 insertBefore method를 사용하여 마지막 노드 전에 새로운 엘리먼트를 삽입하고
		 *마지막 노드는 삭제 합니다. 
		 */
		this.eContainer.parentNode.removeChild(this.eContainer);
	}else if(this.eContainer.nodeType == 1){
		this.roop(this.eContainer, value);
	}
	this.endFlag = true;
	this.startFlag = false;
}
/**
 * 처리할 노드의 상위 노드의 이름이 span일 경우에 처리할 노드가 상위 노드의 문자열을 전부 포함 하고 있다면 
 * 스타일을 변경 해주는 역활을 합니다.
 * IE 전용
 * @param node : 상위 노드 이름이 span인 노드 - object
 * @param value : 글꼴 크기 값 또는 글꼴 종류 - String
 */
EditorManager.prototype.style_Change = function(node, value){
	var textRange = this.oRange.duplicate();
	textRange.moveToElementText(node);
	textRange.moveStart('character',1);
	textRange.moveStart('character',-1);
	textRange.moveEnd('character',-1);
	textRange.moveEnd('character',1);
	var inRange = this.oRange.inRange(textRange);
	if(inRange){
		this.sCommand == 'fontSize'?node.style.fontSize = value+'pt':node.style.fontFamily = value;
		return true;
	}else return false;
}
/**
 * 선택된 문자열 범위 중에 첫번째에 위치한 노드와 그 노드에 어디 부터 선택 되었는지를 찾아 주는 역활을 합니다.
 * IE 전용
 *  
 */
EditorManager.prototype.getsOffset = function() {
	this.setRange();	
	this.oRangeClone.collapse(true);
	var parent = this.oRangeClone.parentElement();
	var siblings = parent.childNodes;
	var testRange;
	for(var i=0; i<siblings.length; i++){
		var child = siblings[i];
		if(child.nodeType == 1){
			testRange = this.oRangeClone.duplicate();
			testRange.moveToElementText( child );
			testRange.collapse();	//default값은 true 이며 시작점을 가르킨다
			var comparison = testRange.compareEndPoints( 'StartToStart', this.oRangeClone);	
			/**
			 *	testRange의 시작점과 this.oRangeClone의 시작점을 비교 한다
			 *	1일 경우는 testRange의 시작점이 오른쪽에 위치 할 때
			 *	0일 경우 testRange의 시작점이랑 비교할 Range의 시작점이 같을 경우
			 *	-1일 경우 testRange의 시작점이 왼쪽에 위차 할 때 
			 * */
			if ( comparison > 0 ){	//testRange 의 시작점이 oRangeClone의 시작점 보다 뒤에 있을때
				break;
			}else if ( comparison == 0 ){	// 같을 때는 부모 엘리먼트의 첫번째 시작점과 range의 시작 점이 같을 때이다.
				this.sContainer = parent;
				this.sOffset = i;
			}
			testRange = null;
		}
	}
	if(!testRange){	
		testRange = this.oRangeClone.duplicate();
		testRange.moveToElementText( parent );
		testRange.collapse( false );	
	}
	testRange.setEndPoint( 'StartToStart', this.oRangeClone );	
	var distance = testRange.text.length;

	while ( distance > 0 ){
		if(siblings[--i].nodeType == 1){	
			distance -= siblings[i].innerText.length;
		}else{
			distance -= siblings[i].nodeValue.length;
		}
	}
	
	if ( distance == 0 ){
		this.sContainer = siblings[i];
		this.sOffset = -distance;
	}else{
		this.sContainer = siblings[i];
		this.sOffset = -distance;
	}
	if(typeof(this.sContainer) == 'undefined'){
		if(parent.nextSibling){
			this.sContainer = parent.nextSibling;
			this.sOffset = 0;
		}else{
			this.sContainer = siblings[--i];
			this.sOffset = 0;
		}
	}
}
/**
 * 선택된 문자열 범위 중에 끝에 위치한 노드와 그 노드에 어디 까지 선택 되었는지를 찾아 주는 역활을 합니다.
 * IE 전용
 */
EditorManager.prototype.geteOffset = function() {
	this.setRange();	
	this.oRangeClone.collapse(false);	
	var parent = this.oRangeClone.parentElement();
	var siblings = parent.childNodes;
	var testRange;
	for(var i=siblings.length-1; i>=0; i--){
		var child = siblings[i];
		if(child.nodeType == 1){
			testRange = this.oRangeClone.duplicate();
			testRange.moveToElementText( child );
			testRange.collapse(false);	
			var comparison = testRange.compareEndPoints( 'EndToEnd', this.oRangeClone);	
			if ( comparison < 0 ){	
				break;
			}else if ( comparison == 0 ){	
				this.eContainer = parent;
				this.eOffset = i;
			}
			testRange = null;
		}
	}
	if(!testRange){	
		testRange = this.oRangeClone.duplicate();
		testRange.moveToElementText( parent );
		testRange.collapse();
	}
	testRange.setEndPoint( 'EndToEnd', this.oRangeClone );
	var distance = testRange.text.length;
	
	while ( distance > 0 ){
		if(siblings[++i].nodeType == 1){
			distance -= siblings[i].innerText.length;
		}else{
			distance -= siblings[i].nodeValue.length;
		}
	}
	if ( distance == 0 ){
		this.eContainer = siblings[i];
		if(siblings[i].nodeType == 1) this.eOffset = siblings[i].innerText.length;
		else this.eOffset = siblings[i].nodeValue.length;
	}else{
		this.eContainer = siblings[i];
		if(siblings[i].nodeType == 1) this.eOffset = siblings[i].innerText.length + distance;
		else this.eOffset = siblings[i].nodeValue.length +distance;
	}
}
/**
 * 첫번째 노드와 마지막 노드를 각각의 offset으로 range에 포함된 부분만 분리하는 역활을 합니다.
 * @param startNode : 선택한 부분의 첫번째 노드 - object
 * @param endNode : 선택한 부분의 마지막 노드 - object
 * @param startOffset : 첫번째 노드의 offset - number
 * @param endOffset : 마지막 노드의 offset - number
 */
EditorManager.prototype.IEsplitNode = function(startNode, endNode, startOffset, endOffset) {
	if(startNode == endNode){
		var textNode = startNode.splitText(startOffset);
		textNode.splitText(endOffset-startOffset);
		this.sContainer = textNode;
		this.eContainer = this.sContainer;
	}else{
		if(startNode.nodeType == 3){
			textNode = startNode.splitText(startOffset);
			this.sContainer = textNode;
		}
		if(endNode.nodeType == 3){
			endNode.splitText(endOffset);
			this.eContainer = endNode;
		}
	}
	
}
/**
 * 파라미터로 받은 노드에 하위 노드 중에 선택된 문자열 범위에 첫번째 노드와 마지막 노드가 포한 되는지 확인 하는 역활을 합니다. 
 * IE에서는 range.parentElement()라는 메서드를 이용하여 선택된 문자열 을 포함하는 상위 노드를 구 할 수있는데 경우에 따라 상위 노드를 구하지 못하는 경우가
 * 생기기 때문에 이 메서드를 사용 합니다.
 * IE 전용
 * 
 * 예) 여러 단락을 선택 하고 range의 끝 부분이 단락에 마지막에 있는 빈공간에 위치 했을 경우(IE에 경우 단락 끝 부분에 빈 공간이 있다)
 * @param parent : this.oRange.parentElement() 메서드에서 반환 된 노드 - object
 */
EditorManager.prototype.getParentElement = function(parent) {
	var childes = parent.childNodes;
	for(var i=0; i<childes.length; i++){
		if(childes[i] == this.sContainer){
			this.startFlag = true;
		}
		if(childes[i] == this.eContainer){
			this.endFlag = true;
		}else if(childes[i].nodeType == 1){
			if(childes[i].childNodes){
				this.getParentElement(childes[i]);
			}
		}
		if(this.endFlag){
			break;
		}
	}
}
