function Member(grpid, nickname, userid, avatarid, profileid, rolecode, servername) {
	this.grpid = grpid;
	this.nickname = nickname;
	this.userid = userid;
	this.avatarid = avatarid;
	this.profileid = profileid;
	this.rolecode = rolecode;
	this.servername = servername;
}
var CafeOnAjax = {
	sendRequest: function(method, url, param, fSuccess, fFail) {
		var xmlHttp = null;
		
		if(window.XMLHttpRequest) {
			xmlHttp = new XMLHttpRequest();
		} else if (window.ActiveXObject) {
			xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
		}
		if(xmlHttp != null){
			xmlHttp.onreadystatechange = function() {
				if(xmlHttp.readyState == 4) {
					if(xmlHttp.status == 200) {
						var ct = xmlHttp.getResponseHeader("content-type");
						var data = (ct && ct.indexOf("xml") >= 0) ? xmlHttp.responseXML : xmlHttp.responseText;
						fSuccess(data);
					} else {
						fFail(xmlHttp);
					}
				}
			};
			if(url != null && url != "") {
				if(method.toUpperCase() == "GET") {
					xmlHttp.open("GET", url, true);
					xmlHttp.setRequestHeader("X-Requested-With", "XMLHttpRequest");
					xmlHttp.send(null);
				}
			}
		}
	},
	checkUserDataAjax: function(grpid, userid, rolecode, nickname){
		var loggingUrl = "/_c21_/check_validmember?grpid=" + grpid + "&userid=" + userid + "&rolecode=" + rolecode;
		this.sendRequest("GET", loggingUrl, null,
				function (state){
					if(state == "false"){
						CafeOnAjax.requestLoggingAjax(grpid, nickname, "userid_" + userid + "_rolecode" + rolecode);
					}
				}, function (){});	
	},	
	requestLoggingAjax: function(grpid, nickname, msg){
		var loggingUrl = "/_c21_/client_logging?grpid=" + grpid + "&name=" + encodeURIComponent(nickname) + "&msg=" + msg;
		this.sendRequest("GET", loggingUrl, null, function(){}, function(){});	
	}
}
var cafeonManager = {
	roleIconType: null,
	grpid: null,
	mgrpid: null,
	cafeNode: null,
	cafeName: null,
	cafeonState: null,
	cafeonServer: null,
	cafeonPort: null,
	grpcode: null,
	memberCount: 0,
	isUserCountCheck: false,
	changeServerFlag: false,
	searchCallbackFunction: null,
	searchCallbackScopeObj: null,
	/*
	 * getter, setter
	 */
	getCafeName: function() {
		return this.cafeName;
	},
	setCafeonServer: function(server) {
		if (this.cafeonServer != server && this.isConnected()) {
			this.changeServerFlag = true;
		}

		this.cafeonServer = server
	},
	getCafeonServer: function() {
		return this.cafeonServer;
	},
	setCafeonState: function(state) {
		this.cafeonState = state;
	},
	getCafeonState: function() {
		return this.cafeonState;
	},
	setRoleIconType: function(roleIconType) {
		this.roleIconType = roleIconType;
	},
	getRoleicontype: function() {
		return this.roleIconType;
	},
	setCafeonPort: function(cafeonPort) {
		this.cafeonPort = cafeonPort;
	},
	getCafeonPort: function() {
		return this.cafeonPort;
	},
	setIsUserCountCheck: function(flag) {
		this.isUserCountCheck = flag;
	},
	/*
	 * init
	 */
	init: function(grpid, mgrpid, cafeNode, cafeName, grpcode) {
		this.grpid = grpid;
		this.mgrpid = mgrpid;
		this.cafeNode = cafeNode;
		this.cafeName = cafeName;
		this.grpcode = grpcode;
	},
	getCafeonFlashFrame: function() {
		return frames.cafeon2009;
	},
	getCafeFrame: function() {
		return frames.down;
	},
	
	documentObject: null,
	tempObject: null,
	documentCntObject: null,
	
	setCounterObject: function(object) {
		this.documentCntObject = object;
	},
	logoutCafeon: function() {
		try {
			this.clearCafeon();
			this.getCafeonFlashFrame().logout();
			
			if(this.documentObject != null) {
				this.errorMessage("·Î±×¾Æ¿ôµÇ¾î ¸ñ·ÏÀ» Ç¥½ÃÇÒ ¼ö ¾ø½À´Ï´Ù.");
			}
		} catch (e) {
		}
	},
	
	maxRetry: 3,	
	retry: 0,
	loadCafeon: function(object, grpid, userid, nickname, rolecode, avatarid, profileid) {
		if (this.changeServerFlag) {
			this.changeServerFlag = false;
			this.logoutCafeon();
			return;
		}
		
		try {
			this.getCafeonFlashFrame().refresh();
		} catch(e) {}
		
		try {
			top.thisMovie("CafeOnFlash").logOffCafeOn();
			top.loding = false;
		} catch(e) {}
		
		var chatState = this.getCafeFrame().document.getElementById("cafeon_chk");
		
		if (this.getCafeonState() == "private") {
			chatState.checked = true;
		}
		
		if(!(userid == "" || grpid == "")) {
			this.setMyCeUserid(userid);
			this.documentObject = object;
			this.tempObject = this.getCafeonFlashFrame().document.getElementById("cafeonUserList");			
		
			if(this.isConnected()) {
				this.loadComplete();	
				$(this.documentObject).html("");
				window.setTimeout('cafeonManager.getUserList()', 600);
			} else {
				try {
					this.clearCafeon();
					
					if(this.getCafeonState() != "private") {
						this.setCafeonState("public");
					}

					//nickname = nickname.replace(/ /g, "_");
					this.getCafeonFlashFrame().login(grpid, userid, nickname, rolecode, avatarid, profileid, this.getCafeonState());
				} catch(e) {
					
					if(this.retry <= this.maxRetry) {
						this.getCafeFrame().loadCafeOn();
						this.retry++;
					} else {
						if (!this.getCafeonFlashFrame().hasRequestedVersion) {
							this.loadComplete();
							this.errorMessage("Flash Player ¹öÀüÀÌ ³·°Å³ª Á¤»óÀûÀ¸·Î ¼³Ä¡µÇÁö ¾Ê¾Æ 'Ä«Æä¿Â'À» ½ÇÇàÇÒ ¼ö ¾ø½À´Ï´Ù. ÃÖ½Å ¹öÀüÀÇ Flash Player¸¦ ¼³Ä¡ÇÏ¼¼¿ä.");
						} else {
							this.loadComplete();				
							this.errorMessage("CafeonÀÌ ·Îµå µÇÁö <br>¾Ê¾Ò½À´Ï´Ù.ÀçÁ¢¼ÓÀ» <br>Å¬¸¯ÇØÁÖ¼¼¿ä<div align='center'>[<a href=\"javascript:cafeonManager.clearCafeon();loadCafeOn();\">ÀçÁ¢¼Ó</a>]</div>");
						}
					}
				}
			}
		}
	},
	isConnected: function() {
		try {
			var isConn = this.getCafeonFlashFrame().isConnected();
			return isConn;
		} catch(e) {
			return false;
		}
	},
	connectionTimeout: function() {
		this.getCafeonFlashFrame().logout();
		this.clearCafeon();
		this.errorMessage("·Î±×¾Æ¿ôµÇ¾î ¸ñ·ÏÀ» Ç¥½ÃÇÒ ¼ö ¾ø½À´Ï´Ù.");

	},
	loadComplete: function() {
		try { 
			$(this.documentObject).show();
			$(this.getCafeFrame().document.getElementById("cafeOnLoging")).hide();
		} catch(e) {
			$(this.documentObject).show();
		}
	},
	
	myCeUserid: "",
	setMyCeUserid: function(userid) {
		this.myCeUserid = userid;
	},
	getMyCeUserid: function() {
		return this.myCeUserid;
	},
		
	memberArray: new Array(),
	mbrListCnt: 0,
	
	addUser: function(grpid, nickname, userid, avatarid, profileid, rolecode, servername, isMy) {
		/*TODO
		Ä«Æä¿Â¿¡ Á¢¼Ó ¼º°ø ÈÄ µ¹¾Æ¿À´Â À¯ÀúÀÇ Á¤º¸°¡ º¯°æµÇ¾ú´ÂÁö ¿©ºÎ °Ë»ç
		*/
		if((down.CAFEAPP.GRPID == "16zhl" || down.CAFEAPP.GRPID == "1Ndrc" || down.CAFEAPP.GRPID == "w3R2") && nickname != "¼Õ´Ô"){
			CafeOnAjax.checkUserDataAjax(down.CAFEAPP.GRPID, userid, rolecode, nickname);
		}
		if (($.grep(cafeonManager.memberArray, function(n,i) { return n.userid == userid; })).length <= 0) {
			if (this.mbrListCnt <= 50) 
			{
				this.memberArray[this.mbrListCnt++] = new Member(grpid, nickname, userid, avatarid, profileid, rolecode, servername);
				this.displayMember(grpid, nickname, userid, userid, avatarid, profileid, rolecode);
				
				try {
					if (this.documentObject) {
						if(isMy && this.getCafeonState() == "private") {
							if ($(this.documentObject).children("#MYID")) {
								$(this.documentObject).children("#MYID").fadeTo("slow", 0.33);
							}
						}						
						$(this.documentObject)[0].scrollTop = $(this.documentObject)[0].scrollHeight;
					}
				} catch(e) {}
			}
		}
	},
	deleteLayer: function(userListID) {
		try {
			if (this.getCafeFrame().document.getElementById(userListID) != null) {
				$(this.getCafeFrame().document.getElementById(userListID)).remove();
			}
			if (this.getCafeonFlashFrame().document.getElementById(userListID) != null) {
				$(this.getCafeonFlashFrame().document.getElementById(userListID)).remove();
				$(this.documentObject)[0].scrollTop = $(this.documentObject)[0].scrollHeight;
			}
		} catch(e) {}	
	},
	deleteUser: function(delUserId) {
		if (delUserId != this.getMyCeUserid()) {
			this.deleteLayer(delUserId);
				
			var usrIdx = 0;
			var arraySize = this.memberArray.length;
			var findUsr = false;

			for (var i = 0; i < arraySize; i++) {
				
				if (this.memberArray[i].userid == delUserId) {
					usrIdx = i;
					findUsr = true;
					break;
				}
			}
	
			var tmpArrayHead = new Array();
			var tmpArratTail = new Array();
			
			if(findUsr == true && arraySize > 1) {
				if (usrIdx == 0) {
					this.memberArray = this.memberArray.slice(1, arraySize);
				} else if (usrIdx == this.memberArray.length) {
					this.memberArray = this.memberArray.slice(0, arraySize - 1);
				} else {
					tmpArrayHead = this.memberArray.slice(0, usrIdx);
					tmpArrayTail = this.memberArray.slice(usrIdx + 1, arraySize)
					this.memberArray = tmpArrayHead.concat(tmpArrayTail);
				}
				this.mbrListCnt--;
			} else if (findUsr == true && arraySize == 1) {
				this.memberArray = "";
				this.mbrListCnt--;
			}
		}
	},
	requestTalk: function(target, profileid, userid, nickname, rolecode) {
		var usrIdx = 0;
		var user = ($.grep(cafeonManager.memberArray, function(n, i) { return n.userid == userid; }));
		if(user.length > 0) {
			userid = user[0].userid;
			
			var ircnick = "";
			try{
				ircnick = cafeon.GetMemberINick(usrIdx);
			} catch(e){}
			
			if ($(target).parent().attr("id") == $(this.documentObject).attr("id")) {
				this.getCafeFrame().autolayer_on(target, profileid, userid, ircnick, nickname, rolecode);
			} else {
				this.getCafeFrame().chat_home_if.autolayer_on(target, profileid, userid, ircnick, nickname, rolecode);
			}
		}
	},
	requestTalk2: function(target, profileid, userid, nickname, rolecode) {
		this.getCafeFrame().chat_home_if.autolayer_on(target, profileid, userid, "", nickname, rolecode);
	},
	addUserLayer: function(member) {
		$(this.tempObject).append(member);
		$(this.documentObject).append(member);
	},
	displayMember: function(grpid, nickname, userid, encuserid, avatarid, profileid, rolecode) {
		try {
			var dispNickname;
			
			if( nickname.length >= 10) {
				dispNickname = nickname.substring(0, 10) + "..";
            } else {
            	dispNickname = nickname;
            }
			
			var roleIconType = this.getRoleicontype();
	
			if (rolecode == "31") {
				roleIconType = "";
			}
			
			var member = "";
				if(userid != this.getMyCeUserid()) {
					member = "<li id='" + encuserid + "' onclick=\"cafeonManager.requestTalk(this,'" + profileid + "','" + userid + "','" + nickname + "','" + rolecode + "');\" onmouseout=\"autolayer_off(this);\" class='hand' ><img src='http://cafeimg.hanmail.net/cf_img2/bbs2/" + roleIconType + "_level_" + rolecode + ".gif' width='13' height='13' alt='' class='imgR' /><span onDblclick=\"goP2pChat('" + nickname + "','" + userid + "')\">" + dispNickname + "</span></li>";
				} else {
					this.privateMyDisplay = "<img src='http://cafeimg.hanmail.net/cf_img2/bbs2/" + roleIconType + "_level_" + rolecode + ".gif' width='13' height='13' alt='' class='imgR' />" + dispNickname + "&lt;<span id='mystate'>À¯·É</span>&gt;";
					this.publicMyDisplay = "<img src='http://cafeimg.hanmail.net/cf_img2/bbs2/" + roleIconType + "_level_" + rolecode + ".gif' width='13' height='13' alt='' class='imgR' />" + dispNickname + "&lt;<span id='mystate'>³ª</span>&gt;";
					
					if(this.getCafeonState() == "private") {
		 				member = "<li id='MYID' class='hand' >" + this.privateMyDisplay + "</li>";
		 			} else {
						member = "<li id='MYID' class='hand' >" + this.publicMyDisplay + "</li>";	 				
		 			}
		 		}
				this.addUserLayer(member);
		} catch(e) {}
	},
	displayCount: function(memberCount) {
		try {
			this.memberCount = memberCount;
			$(this.documentCntObject).html("" + this.memberCount + "");
		} catch(e) {}
	},
	tempLists: null,
	getUserList: function() {
		this.tempLists = $(this.tempObject).html();
		
		try {
			if (this.documentObject != null) {
				$(this.documentObject).html(this.tempLists);
				this.displayCount(this.getCafeonFlashFrame().getUserCount());
			}
			
			var chatState = this.getCafeFrame().document.getElementById("cafeon_chk");
			
			if(this.getCafeonState() == "public") {
				this.publicMyId();
				chatState.checked = false;
			} else {
				this.privateMyId();
				chatState.checked = true;
			}
		} catch(e) {}		
	},
	getUserCount: function() {
		return this.getCafeonFlashFrame().getUserCount();
	},
	clearCafeon: function() {
		this.memberArray = null;
		this.memberArray = [];
		this.mbrListCnt = 0;		
		if (this.documentObject != null ) {
			try {
				$(this.documentObject).html("");
				$(this.documentCntObject).html("0");
			} catch (e) {}
		}
		if (this.tempObject != null) {
			try {
				$(this.tempObject).html("");
			} catch (e) {}
		}		
	},
	errorMessage: function(mesaage) {
		$(this.documentObject).html(mesaage);
	},
	disconnected: function(flag) {
		this.clearCafeon();
		this.errorMessage("¼­¹ö¿Í Á¢¼ÓÀÌ ²÷¾îÁ³½À´Ï´Ù.<div align='center'><a href=\"javascript:cafeonManager.clearCafeon();loadCafeOn();\">ÀçÁ¢¼Ó</a>");				
	},
	sendUserState: function (inputBox) {
		var chatState = this.getCafeFrame().document.getElementById(inputBox);
		
		if (this.getCafeonState() == "public") {
			this.setCafeonState("private");
			this.privateMyId();
			
			chatState.checked = true; 
			
			var message = "private";
			this.getCafeonFlashFrame().setPrivate(message);
		} else {
			this.setCafeonState("public");
			this.publicMyId();
			
			var message = "public";
			this.getCafeonFlashFrame().setPrivate(message);
		}
	},
	privateMyId: function() {
			$(this.documentObject).children("#MYID").html(this.privateMyDisplay);
			$(this.documentObject).children("#MYID").fadeTo("slow",0.33);
					
			$(this.tempObject).children("#MYID").html(this.privateMyDisplay);
			$(this.tempObject).children("#MYID").fadeTo("slow", 0.33);
	},
	publicMyId: function() {
		$(this.documentObject).children("#MYID").html(this.publicMyDisplay);
		$(this.documentObject).children("#MYID").fadeTo("slow", 1);
		
		$(this.tempObject).children("#MYID").html(this.publicMyDisplay);
		$(this.tempObject).children("#MYID").fadeTo("slow", 1);
	},
	searchUser: function(nickname, callback, scopeObject) {
		this.searchCallbackFunction = callback;
		this.searchCallbackScopeObj = scopeObject;
		this.getCafeonFlashFrame().searchUser(nickname);
	},
	completeSearch: function(userList) {
		if (this.searchCallbackFunction == null) {
			this.getCafeFrame().searchCafeonShowUser(userList);
		} else {
			this.searchCallbackFunction.call(this.searchCallbackScopeObj, userList);
		}
	},
	debugLog: function(message) {
		try {
			console.log("%s: %o", message, this);
		} catch(e) {
		}
	},
	// [CafeChat2009] 1:1 ´ëÈ­ÇÏ±â¸¦ ÇßÀ» °æ¿ì È£ÃâµÇ´Â ¸Þ¼Òµå. Ã¤ÆÃ¹æÀ» ¸¸µé°í »ó´ë¹æÀ» ÃÊ´ë.
	sendP2PChatRequest: function(grpcode, grpid, myNickName, dearNickName, myUserID, dearUserID, rolcode) {
			
		if (rolcode <= "22") {
			alert("1:1 ´ëÈ­´Â Á¤È¸¿ø ÀÌ»ó¸¸ ½ÅÃ»ÇÒ ¼ö ÀÖ½À´Ï´Ù.");
		} else {
			var windowName = Math.floor((Math.random() * 20) + 9);

			// create popup
			var popup = window.open("", windowName, "toolbar=no, scrollbars=no, resizable=yes, width=395, height=495");
			
			if(!popup) {
				alert("1:1´ëÈ­ ¿äÃ»À» ÇßÀ¸³ª\n ÆË¾÷ÀÌ Â÷´ÜµÇ¾îÀÖ½À´Ï´Ù. ÆË¾÷Â÷´ÜÀ» ÇØÁ¦ÇØ ÁÖ½Ã±â ¹Ù¶ø´Ï´Ù");
			} else {
				// create form.
				var popupForm = document.createElement('form');
				document.body.appendChild(popupForm);
				
				// form attribute setting.
				popupForm.name = "sendP2PChatRequestForm";
				popupForm.action = "http://" + this.cafeNode + ".daum.net/_c21_/chat_create?grpid=" + this.grpid;
				popupForm.method = "post";
				popupForm.target = windowName;

				// input field setting.
				this._addInputField(popupForm, "targetUserid", dearUserID);
				this._addInputField(popupForm, "targetNickname", Base64.encode(dearNickName));
				this._addInputField(popupForm, "isOne2One", "true");
				
				// submit.
				popupForm.submit();
			}
		}
	},
	// [CafeChat2009] ½ÅÃ»ÀÚ : »ó´ë¹æÀ» ÃÊ´ëÇÏ´Â ¸Þ½ÃÁö¸¦ Ä«Æä¿Â¿¡ Àü¼Û.(chat client -> cafeon)
	sendNewP2PChatReq: function(inviteMsg) {
		this.getCafeonFlashFrame().requestNewP2PChat(inviteMsg);
	},
	// [CafeChat2009] ½ÅÃ»ÀÚ : ½ÅÃ»ÇÏ°í ³ª¼­ callback.
	completeP2PChatRequest: function(msg, windowName) {
		// TODO 200ÀÏ°æ¿ì ½ºÅµ, ³ª¸ÓÁö °æ¿ì Ã¤ÆÃÅ¬¶óÀÌ¾ðÆ®¿¡ ³ëÆ¼.
	},
	// [CafeChat2009] ÇÇ½ÅÃ»ÀÚ : Ä«Æä¿Â¿¡¼­ ÃÊ´ë¹ÞÀº ¸Þ½ÃÁö¿¡ µû¶ó ÆË¾÷ »ý¼º.(cafeon -> chat client)
	invite: function(roomParam, windowName) {

		// create popup.
		var popup = window.open("", windowName, "toolbar=no, scrollbars=no, resizable=yes, width=395, height=495");
		
		if(!popup) {
			alert("1:1´ëÈ­ ¿äÃ»ÀÌ ¿ÔÀ¸³ª\n ÆË¾÷ÀÌ Â÷´ÜµÇ¾îÀÖ½À´Ï´Ù. ÆË¾÷Â÷´ÜÀ» ÇØÁ¦ÇØ ÁÖ½Ã±â ¹Ù¶ø´Ï´Ù");
		} else {
			// create form.
			var popupForm = document.createElement('form');
			document.body.appendChild(popupForm);
			
			// form attribute setting.
			popupForm.method = "post";
			popupForm.target = windowName;
			
			// input field setting.
			var isOne2One = false;
			var items = roomParam.split("&");
			for (var i = 0, n = items.length; i < n; i++) {
				var str = items[i];
				var idx = str.indexOf("=");
				var key = str.substring(0, idx);
				var value= str.substring(idx + 1, str.length);
				
				if (idx > -1) {
					this._addInputField(popupForm, key, value);
				}
				
				if (key == "isOne2One" && value == "true") {
					isOne2One = true;
				}
			}
			
			var actionPrefix = "http://" + this.cafeNode + ".daum.net/_c21_/";
			var actionSuffix = "?grpid=" + this.grpid;
			if (isOne2One) {
				popupForm.name = "one2oneForm";
				popupForm.action = actionPrefix + "chat_one2one_popup" + actionSuffix;
			} else {
				popupForm.name = "inviteForm";
				popupForm.action = actionPrefix + "chat_invite_popup" + actionSuffix;
			}
				
			// submit.
			popupForm.submit();
		}
	},
	getUserListForChat: function() {
		return this.memberArray;
	},
	getMemberCount: function() {
		return this.memberCount;
	},
	
	chatMemberArray: new Array(),
	chatMbrCnt: 0,
	
	getUserListForPtoP: function() {
		return this.memberArray;
	},
	goProfile: function(euserid) {
		down.window.goProfile(this.grpid, this.mgrpid, euserid, this.cafeNode);
	},
	checkChatUser: function(addUserId) {
		var findUser = 0;
		
		for(var i = 0; i < this.chatMemberArray.length; i++) {
			if (this.chatMemberArray[i] == addUserId) {
				findUser = 1;
				break;
			}
		}
		
		return findUser;
	},
	addChatUser: function(addUserId) {
		this.chatMemberArray = new Array();
		this.chatMemberArray[this.chatMbrCnt++] = addUserId;
		this.chatMbrCnt++;
	},
	deleteChatUser: function(delUserId) {
		var usrIdx = 0;
		var arraySize = this.chatMemberArray.length;
		var findUsr = false;
		
		for (var i = 0; i < arraySize; i++) {
			if (this.chatMemberArray[i] == delUserId) {
				usrIdx = i;
				findUsr = true;
				break;
			}
		}

		var tmpArrayHead = new Array();
		var tmpArratTail = new Array();
		
		if (findUsr == true && arraySize > 1) {
			if (usrIdx == 0) {
				this.chatMemberArray = this.chatMemberArray.slice(1, arraySize);
			} else if (usrIdx == this.chatMemberArray.length) {
				this.chatMemberArray = this.chatMemberArray.slice(0, arraySize-1);
			} else {
				tmpArrayHead = this.chatMemberArray.slice(0, usrIdx);
				tmpArrayTail = this.chatMemberArray.slice(usrIdx+1, arraySize)
				this.chatMemberArray = tmpArrayHead.concat(tmpArrayTail);
			}
			this.chatMbrCnt--;			
		} else if (findUsr == true && arraySize == 1) {
			this.chatMemberArray = new Array();
			this.chatMbrCnt--;
		}
	},
	replaceAll: function(temp, searchStr, replaceStr) {
		while(temp.indexOf( searchStr ) != -1) {
			temp = temp.replace(searchStr, replaceStr);
		}
		
		return temp;
	},
    
    _addInputField : function (_form, _name, _value) {
		var tmp = document.createElement('input');
		tmp.setAttribute("type", "hidden");
		tmp.setAttribute("name", _name);
		tmp.setAttribute("value", _value);
		_form.appendChild(tmp);
    }
}

/**
*
* Base64 encode / decode
* http://www.webtoolkit.info/
*
**/

var Base64 = {
    // private property
    _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

    // public method for encoding
    encode : function (input) {
        var output = "";
        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        var i = 0;

        input = Base64._utf8_encode(input);

        while (i < input.length) {

            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }

            output = output +
            this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
            this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

        }

        return output;
    },

    // public method for decoding
    decode : function (input) {
        var output = "";
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0;

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < input.length) {

            enc1 = this._keyStr.indexOf(input.charAt(i++));
            enc2 = this._keyStr.indexOf(input.charAt(i++));
            enc3 = this._keyStr.indexOf(input.charAt(i++));
            enc4 = this._keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output = output + String.fromCharCode(chr1);

            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }

        }

        output = Base64._utf8_decode(output);

        return output;

    },

    // private method for UTF-8 encoding
    _utf8_encode : function (string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }

        }

        return utftext;
    },

    // private method for UTF-8 decoding
    _utf8_decode : function (utftext) {
        var string = "";
        var i = 0;
        var c = c1 = c2 = 0;

        while ( i < utftext.length ) {

            c = utftext.charCodeAt(i);

            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            }
            else if((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i+1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else {
                c2 = utftext.charCodeAt(i+1);
                c3 = utftext.charCodeAt(i+2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }

        }

        return string;
    }

}

