//  Keyring Installer object
//  * require: krins.cab(ver:1.0.0.20 and above) for using ActiveX
if(!KRNS) {var KRNS = new Object();}

var KRInsObj = function() {
  this.version = "0.47";
  this.check_activex_result = "";
}

KRInsObj.prototype = {
 
  /////////////////////////////////////////////////////////
  //  ActiveX event handler
  onDownload:function(url, path) {
  },
  onError:function(event) {
    _kractivex_.status = "Error";
    if(typeof(event) == "string") {
      _kractivex_.error_message = event;
    }
  },
  onCancel:function() {
    _kractivex_.status = "Cancel";
  },
  onSuccess:function() {
    _kractivex_.status = "Success";
  },

  /////////////////////////////////////////////////////////
  //  set/get ActiveX property
  //  *you have to call writeActiveX() before this function.
  setLicenseServer:function(server) {
    _kractivex_.server = server;
  },
  setAppName:function(name) {
    _kractivex_.appName = name;
  },
  setAppType:function(type) {
    //  *must be "Type". lower-case "type" is invalid.
    _kractivex_.Type = type;  //  tyep = 0 : KeyringPDF, type = 1 : KeyringLibrary
  },
  getStatus:function() {
    return _kractivex_.status;
  },
  getErrorMessage:function() {
    return _kractivex_.error_message;
  },
  getInstalledVer:function() {
    return _kractivex_.ver_local; //  set by checkAppVersion()
  },
  getLatestVer:function() {
    return _kractivex_.ver_server;  //  set by checkAppVersion()
  },

  /////////////////////////////////////////////////////////
  //  <obect> tag and <embed> tag.
  
  //  write <object> tag for ActiveX.
  //  element: tag-id for <object>.
  writeObjectTag:function(element, id, class_id, code_base, params) {
    //  create <object> string
    var object_tag = '<object id="' + id + '" width="0" height="0" classid="' + class_id + '" codebase="' + code_base + '">';
    for(var key in params) {
      object_tag += '<param name="' + key + '" value="' + params[key] + '" />';
    }
    object_tag += '</object>';
    //  write
    if(!element) {
      document.write(object_tag);
    } else {
      var target = (typeof element=="string")?document.getElementById(element):element;
      target.innerHTML = object_tag;
    }
  },

  //  write <embed> tag.
  //  element: tag-id for <embed>. if not specified, using document.write()
  writeEmbedTag:function(element, id) {
    var embed_tag = '<embed id="' + id + '" width="0" height="0" type="application/x-keyringclient">';
    if(!element) {
      document.write(embed_tag);
    } else {
      var target = (typeof element=="string")?document.getElementById(element):element;
      target.innerHTML = embed_tag;
    }
  },
  
  /////////////////////////////////////////////////////////
  //  ActiveX Install

  //  begin ActiveX installation.
  //  codebase: codebase for <object>.
  //  element: tag-id for <object>. if not specified, using document.write()
  //  handler: event handler.
  writeActiveX:function(codebase, element, handler) {
      params = new Object();
      this.writeObjectTag(element, "_kractivex_", "CLSID:92EB41C4-A271-42C5-999A-4816A3CFCB27", codebase, params);
      _kractivex_.attachEvent("onerror", (handler && handler.onError) ? handler.onError : this.onError);
      _kractivex_.attachEvent("ondownload", (handler && handler.onDownload) ? handler.onDownload : this.onDownload);
      _kractivex_.attachEvent("oncancel", (handler && handler.onCancel) ? handler.onCancel : this.onCancel);
      _kractivex_.attachEvent("onsuccess", (handler && handler.onSuccess) ? handler.onSuccess : this.onSuccess);
  },
      
  //  check update required.	
  //  1. get latest version and installed version.
  //  2. check not installed or old version installed.
  //  return true: not required. false: required.
  //  *you have to call writeActiveX(), setLicenseServer(), setAppName() before this function.
  checkAppVersion:function() {
    try {
      if(_kractivex_.checkVersion() == "OK") {return true;}
    }catch(e) {
    }
    this.checkActiveXStatus();
    return false;
  },
    
  //  install latest version.	
  //  *you have to call writeActiveX() before this function.
  installApp:function(arg) {
    _kractivex_.setup_arg = arg;
    try {
      _kractivex_.install();
    }catch(e) {
    }
    this.checkActiveXStatus();
  },
  
  //  check "_kractivex_.status" and throw Error.
  //  *you have to call writeActiveX() before this function.
  checkActiveXStatus:function() {
  	if(_kractivex_.status == "Error") {
  		throw new Error("[Error]: " + _kractivex_.error_message);
  	} else if(_kractivex_.status == "Cancel") {
  		throw new Error("[Cancel]");
  	}
  },
  
  //  check Adobe Reader or Adobe Acrobat installed. 
  checkAdobeAcrobat:function() {
    try {
      if(_kractivex_.checkReader() == "OK") {return true;}
    }catch(e) {
    }
    this.checkActiveXStatus();
    return false;
  },
  
  //  get installed Adobe Acrobat, Adobe Reader.
  //  *format: [sign][version][separator]
  //   [sign] = "a": AdobeAcrobat, "r": AdobeReader
  //   [version] = "5", "6", "7", "8", "9" 
  //   [separator] = ";"
  getAdobeAcrobat:function() {
    var ret;
    try {
      ret = _kractivex_.getReader();
    }catch(e) {
    }
    this.checkActiveXStatus();
    return ret;
  },

  /////////////////////////////////////////////////////////
  //  Check Browser Plugins

  //  check ActiveX installed.
  //  return true: installed. false: not installed.
  //  * false: set result status to this.check_activex_result.
  isActivexAvailable:function() {
    try{
        //  ActiveX is not installed.
        var plugin = new ActiveXObject("KRMInstaller.InstallerObject");
        if(!plugin) {this.check_activex_result = "Need Install"; return false;}
        //  check update of ActiveX needed. 
        //  if updete nedded, _kractivex_ is just a object.  
        if(_kractivex_.server == undefined) {this.check_activex_result = "Need Update"; return false;}
        //  check ok.
        return true;
      }catch(e){
      }
      return false;
  }, 
  
  //  check KeyringBrowser plugin installed.
  //  return true: installed. false: not installed.
  isKRBrowserPluginAvailable:function() {
    var plugin;
    if(this.getBrowser() == "IE") {
      //  for IE
			try{plugin = new ActiveXObject("KRKeyring.KeyringClient");} catch(e) {}
				if(plugin) {return true;}
			} else {
			//  for Firefox, Safari
      plugin = navigator.plugins["Keyring Plugin"];
      if(plugin) {return true;}
    }
    return false;
  },
  
  //  get KeyringBrowser plugin object.
  getKRBrowserPlugin:function(element) {
    var plugin;
    if(this.getBrowser() == "IE") {
      //  for IE
      try{plugin = new ActiveXObject("KRKeyring.KeyringClient");} catch(e) {}
		} else {
      //  for Firefox, Safari
			navigator.plugins.refresh(false); 
			if(navigator.plugins["Keyring Plugin"]) {
				plugin = document.getElementById("_npkrclient_");
				if(!plugin) {
					this.writeEmbedTag(element, "_npkrclient_");
					plugin = document.getElementById("_npkrclient_");
				}
			}
    }
    return plugin;
  },

  /////////////////////////////////////////////////////////
  //  Check KeyringClient application

	//	check Keyring Client supported.
	//	type	:	1 = KeyringPDF
	//				  2 = KeyringFLASH
	isKeyringClientSupported: function(client_type) {
		var os = this.getOS();
		var browser = this.getBrowser();
		//	KeyringPDF
		if(client_type == 1) {
			if(os == "Windows" && !this.isOldWindows()) {return true;}
		}
		//	KeyringFLASH
		else if(client_type == 2) {
			if(os == "Windows" && !this.isOldWindows()) {
				if(browser == "IE") {return true;}
				else if(browser == "Firefox") {return true;}
				//else if(browser == "Chrome") {return true;}
			}
			else if(os == "Mac") {
				if(browser == "Safari") {return true;}
				else if(browser == "Firefox") {return true;}
				//else if(browser == "Chrome") {return true;}
			}
		}
		return false;
	},
  
	//	get version of the KeyringFLASH client application for current browser
  getCurrentBrowserKeyringFlashClientVersion:function() {
		var ver;
    if(this.getBrowser() == "IE") {
      //  for IE
			try{
				var plugin = new ActiveXObject("KRKeyring.KeyringClient");
				if(plugin) {ver = plugin.getVersion("KeyringFlashDecryptorIE");}
			}catch(e){
			}
    } else {
      //  for Firefox, Safari
			navigator.plugins.refresh(false); 
      var plugin = navigator.plugins["KeyringFLASH Plugin"];
      if(plugin) {ver = plugin.description.replace(/([a-zA-Z]|\s)+/,"");}
    }
    return ver;
  },

	//	get version of the KeyringFLASH client application for specified browser
  getSpecifiedBrowserKeyringFlashClientVersion:function(element, browser) {
		var ver;
		if(browser == "IE") {
			var plugin = this.getKRBrowserPlugin(element);
			try{ver = plugin.getVersion("KeyringFlashDecryptorIE");} catch(e) {}
    }
		else {
			throw new Error("specified browser \"" + browser + "\" is not supported.");
		}
		return ver;
  },
	
  //  get version of the KeyringFLASH client application.
  //  [Windows]
  //    IE: need 1.6.0 and above. 
  //    Firefox: all version ok.
  //  [Mac] 
  //    Firefox, Safari: all version ok.  
  getKeyringFlashClientVersion:function(element, target_browser) {
		var ver;
		if(!target_browser) {
			ver = this.getCurrentBrowserKeyringFlashClientVersion();
		}
		else {
			ver = this.getSpecifiedBrowserKeyringFlashClientVersion(element, target_browser);
		}
    return ver;
  },
    
  //  get version of the KeyringPDF client application.
  //  element: tag-id for <embed>. if not specified, using document.write()
  //  [Windows]
  //     IE: need 4.8.0 and above.
  //     Firefox, Safari: need 4.9.0 and above.
  //  [Mac]
  //     not supported.
  getKeyringPdfClientVersion:function(element) {
    var ver;
    var plugin = this.getKRBrowserPlugin(element);
		try{ver = plugin.getVersion("KRMDecryptor");} catch(e) {}
    return ver;
  },
  
  //  get version of the KeyringLibrary client application.
  //  element: tag-id for <embed>. if not specified, using document.write()
  //  [Windows]
  //     IE: need KeyringPDF 4.8.0 and above.
  //     Firefox, Safari: need KeyringPDF 4.9.0 and above.
  //  [Mac]
  //     not supported.
  getKeyringLibraryVersion:function(element, custom_name) {
    var ver;
    var app_name = custom_name ? "KRMArchiver" + custom_name : "KRMArchiver";
    var plugin = this.getKRBrowserPlugin(element);
		try{ver = plugin.getVersion(app_name);} catch(e) {}
    return ver;
  },

  //  get CID
  //  [Windows]
  //     need KeyringPDF 4.10.4 and above.
  //     need KeyringFLASH for IE 1.8.0 and above.
  //     need KeyringFLASH for Firefox 1.2.0 and above.
  //  [Mac]
  //     need KeyringFLASH 1.2.6 and above.
  getCID:function(element) {
    var cid = "";
		var plugin = this.getKRBrowserPlugin(element);
		try{cid = plugin.getCID();} catch(e) {}
    return cid;
  },
  
  /////////////////////////////////////////////////////////
  //  Check environment 

  //  check staging environment
  //  @param  host  (string, option)host name. default: using "location.host"
  isStaging: function(host) {
    if(!host) {host = location.host;}
    if(host.indexOf("localhost") == 0 || host.indexOf("wwwstg.keyring.net") == 0 ||
      host.indexOf("bookendstg.keyring.net") == 0 || host.indexOf("test.keyring.net") == 0) {
      return true;
    }
    return false;
  },
  
  //  get os 
  //  return "Windows", "Mac", "Other"
  getOS:function() {
    if(navigator.platform.indexOf('Win') != -1) {return "Windows";} 
    else if(navigator.platform.indexOf('Mac') != -1) {return "Mac";} 
    return "Other";
  },

  //  get browser
  //  return "IE", "Opera", "Safari", "Firefox", "Chrome",
  //          or IE compatible "Sleipnir", "Grani", "Donut",
  //          or "Other"
  getBrowser:function() {   
    if(navigator.appName.indexOf("Internet Explorer") != -1) {
      if(navigator.userAgent.indexOf("Sleipnir") != -1) {
        return "Sleipnir";
      }
      else if(navigator.userAgent.indexOf("Grani") != -1) {
        return "Grani";
      }
      else if(navigator.userAgent.indexOf("Donut") != -1) {
        return "Donut";
      }
      return "IE";	
    } 
    else if(navigator.appName.indexOf("Opera") != -1) {
      return "Opera";
    } 
    else if(navigator.appName == "Netscape") { 	
      if(navigator.vendor.indexOf("Apple") != -1) {
        return "Safari";	
      } 
      else if(navigator.vendor.indexOf("Google") != -1) {
        return "Chrome";	
      } 
      else if(navigator.userAgent.indexOf("Firefox") != -1) {
        return "Firefox";
      }
    }
    return "Other";
  },
  
  //  OS is Windows98, 98SE, Me
  isOldWindows:function() {
    if(navigator.platform.indexOf('Win') != -1 && navigator.userAgent.indexOf("Windows NT") == -1) {return true}
    return false;
  },
  
  //  check Adobe Acrobat or Adobe Reader installed.
  //  *cannot get version.
  isAdobeAcrobatIntalled:function() {
		if(this.getBrowser() == "IE") {
			try{
				var plugin = new ActiveXObject("AcroPDF.PDF");
				if(plugin) {return true;}
			}catch(e){
			}
		} else {
      var plugin = navigator.plugins["Adobe Acrobat"];
      if(!plugin) {plugin = navigator.plugins["Adobe Acrobat and Reader Plug-in"];} //  * Mac & Safari only
      if(plugin) {return true;}
    }
    return false;
  },
  
  //  get Flash Player version.
  //  *FlashPlayer 6 and adobe only.
  getFlashPlayerVersion:function() {
    var ver;
		if(this.getBrowser() == "IE") {
			try{
				var plugin = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
				if(plugin) {ver = plugin.GetVariable("$version").split(" ")[1].replace(/,/g,".");}
			}catch(e){
			}
		} else {
      var plugin = navigator.plugins["Shockwave Flash"];
      if(plugin) {ver = plugin.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".");}
    }
    return ver;
  },  

  /////////////////////////////////////////////////////////
  //  Utility 
  
  //  compare version number
  //  *format: "x.x.x.x" (max 4 elements)
  //  return: 
  //    ver1 > ver2 : 1
  //    ver1 == ver2 : 0
  //    ver1 < ver2 : -1
  compareVersion: function(ver1, ver2) {
    var array1 = ver1 ? ver1.split(".") : new Array(0);
    var array2 = ver2 ? ver2.split(".") : new Array(0);
    //  convert to Number
    for(i = 0; i < 4; i++) {
      if(!array1[i]) {array1[i] = 0;}
      if(!array2[i]) {array2[i] = 0;}
      array1[i] -= 0;
      array2[i] -= 0;
    }
    return compare(0);
    //  compare value of index i
    function compare(index) {
      if(array1[index] > array2[index]) {return 1;}
      else if(array1[index] == array2[index]) {return index >= 3 ? 0 : compare(index + 1);}
      else {return -1;}
    }
  }
}
  
/////////////////////////////////////////
//	Utility class methods
  
//  get element object from id attribute
//  @param  element  (string, object)element id or object.
//  @return (object)element object.
KRInsObj.$ = function(element) {
  return (typeof(element) == "string") ? document.getElementById(element) : element;
}
  
//  make human readable strings
//  @param  obj  (...)any object you want to know
//  @param  max  (Number)max length of message(default:200)
//  @return (string)human readable strings of object.
KRInsObj.inspect = function(obj, max) {
  var msg = "";
  if(!max) {max = 200;}
  var type = typeof(obj);
  if(obj === null) {return "null";}
  if(obj === undefined) {return "undefined";}
  switch(type) {
  case "string":
    msg += "\"" + obj + "\"";
    break;
  case "object":
    if(obj.length) {
      msg += "[";
      var first = true;
      for(var i = 0; i < obj.length; i++) {
        if(first) {first = false;} else {msg += ", "}
        msg += KRInsObj.inspect(obj[i], max);
        if(msg.length > max) {msg += "..."; break;}
      }
      msg += "]"
    }
    else {
      msg += "{";
      var first = true;
      for(var key in obj) {
        if(first) {first = false;} else {msg += ", "}
        msg += key + ":" + KRInsObj.inspect(obj[key], max);
        if(msg.length > max) {msg += "..."; break;}
      }
      msg += "}"
    }
    break;
  case "function":
    msg += "function()";
    break;
  default:   
    msg += obj; 
    break;
  }
  return msg;
}

//  copy array
//  @param  src (array)source array
//  @return (array)copied array
KRInsObj.copyArray = function(src) {
  return src.slice(0);
}
  
//  search valur in array
//  @param  array (array)target array
//  @param  value (any)target value
//  @return (number)-1:not found, other:index value  
KRInsObj.searchArray = function(array, value) {
  for(var i = 0; i < array.length; i++) {
    if(array[i] == value) {return i;}
  }
  return -1;
}

//  create <style> tag
//  @param  media   media attribute value
KRInsObj.createSytleElement = function(media) {
  var element = document.createElement('style');
  element.type = 'text/css';
  element.media = media;
  document.getElementsByTagName('head')[0].appendChild(element);
  return document.styleSheets[document.styleSheets.length - 1];
}

//  create <script> tag
//  @param  src   media attribute value
KRInsObj.createScriptElement = function(src) {
  var element = document.createElement('script');
  element.type = 'text/javascript';
  element.src = src;
  document.getElementsByTagName('head')[0].appendChild(element);
}

//  add rule to <style> tag element
//  @param  elem      (object)CSS tag object
//  @param  selector  (string)selector
//  @param  styles    (string)styles
KRInsObj.addStyleRule = function(elem, selector, styles) {
  if(elem.insertRule) {
    elem.insertRule(selector + "{" + styles + "}", elem.cssRules.length);
  } else if(elem.addRule) {
    elem.addRule(selector, styles, elem.rules.length);
  }
}

//  insert new elemet object
//  @param  name  (string)new element tag name
//  @param  attr  (object)new element attributes
//  @param  target (name)element id attribute name of inserted element
//  @param  position (string)where insert element to
//  @note   don't use appendChild(). because browser plugin is not activated.
KRInsObj.insertElement = function(name, attr, target, position) {
  var newElement = document.createElement(name);
  for(key in attr) {
    newElement.setAttribute(key, attr[key]);
  }
  var targetElement = KRInsObj.$(target);
  switch(position) {
  case "top":
    targetElement.insertBefore(newElement, targetElement.firstChild);
    break;
  case "bottom":
    targetElement.insertBefore(newElement, null);
    break;
  case "before":
    targetElement.parentNode.insertBefore(newElement, targetElement);
    break;
  case "after":
    targetElement.parentNode.insertBefore(newElement, targetElement.nextSibling);
    break;
  default: 
    throw new Error("invalid position value"); 
    break;
  }
}

//  generate random number
//  @param  min (number)min value
//  @param  max (number)max value
//  @return (number)random number
KRInsObj.randomNumber = function(min, max) {
  return Math.floor((max - min + 1) * Math.random() + min);
}

//  generate random octets data
//  @param  size  (number)data length
//  @return (array)octets data
KRInsObj.randomOctets = function(size) {
  var octets = new Array();
  for(var i = 0; i < size; i++) { 
    octets.push(KRInsObj.randomNumber(0, 255));
  }
  return octets;
}

//  generate random name(consist of '0'-'f')
//  @param  size  (number)string length
//  @return (string)random name
KRInsObj.randomName = function(size) {
  var name = "";
  for(var i = 0; i < size; i++) {
    name += KRInsObj.randomNumber(0, 15).toString(16);
  }
  return name;
}

/////////////////////////////////////////
//	Base16

//	Byte Array to Base16 Strings
//	@param	octets	(Array)bytes Array
//	@return	(String)Base16 strings
KRInsObj.Base16Enc = function(octets) {
	var base16 = "";
	for(var i = 0; i < octets.length; i++) {
		var tmp = (octets[i] & 0xff).toString(16);
		if(tmp.length == 1) {tmp = "0" + tmp;}
		base16 += tmp;
	}
	return base16;
}

//	Byte Array to UTF8 Strings
//	@param	str	(String)UTF8 strings
//	@return	(String)Base16 strings
KRInsObj.Base16EncFromString = function(str) {
	return KRInsObj.Base16Enc(KRInsObj.StrToOctets(str));
}

//	Base16 Strings to Byte Array
//	@param	str	(String)Base16 strings
//	@return	(Array)bytes Array
KRInsObj.Base16Dec = function(str) {
	var octets = [];
	for(var i = 0; i < str.length; i++) {
		var hex = str.charAt(i) + str.charAt(++i);
		octets.push(parseInt(hex, 16));
	}
	return octets;
}

//	Base16 Strings to UTF8 Strings
//	@param	str	(String)Base16 strings
//	@return	(String)UTF8 strings
KRInsObj.Base16DecToString = function(str) {
	return KRInsObj.OctetsToStr(KRInsObj.Base16Dec(str));
}

/////////////////////////////////////////
//	Base64

//	Byte Array to Base64 Strings
//	@param octets (Array)bytes Array
//	@return	(String)Base64 strings
KRInsObj.Base64Enc = function(octets) {
	var b64char     = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	var b64encTable = b64char.split("");
  
	var md  = octets.length % 3;
	var b64 = "";
	var i, tmp = 0;
       
	if(md) {for(i = 3 - md; i > 0; i--) {octets[octets.length] = 0;}}
       
	for(i = 0; i < octets.length; i += 3) {
		tmp = (octets[i]<<16) | (octets[i+1]<<8) | octets[i+2];
		b64 += b64encTable[(tmp >>>18) & 0x3f] + 
						b64encTable[(tmp >>>12) & 0x3f] + 
						b64encTable[(tmp >>> 6) & 0x3f] +
						b64encTable[tmp & 0x3f];
	}

	if(md) {
		md = 3 - md;
		b64 = b64.substr(0, b64.length - md);
		while(md--) {b64 += "=";}
	}
	
	return b64;
}

//	UTF8 Strings to Base64 Strings
//	@param str	(String)UTF8 strings
//	@return	(String)Base64 strings
KRInsObj.Base64EncFromString = function(str) {
	return KRInsObj.Base64Enc(KRInsObj.StrToOctets(str));
}

//	Base64 Strings to Byte Array
//	@param	str		(String)Base64 strings
//	@return	(Array)bytes Array
KRInsObj.Base64Dec = function(str) {
	var b64char     = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	var b64decTable = [];
	for(var i = 0; i < b64char.length; i++) {b64decTable[b64char.charAt(i)] = i;}

	str = str.replace(/[^A-Za-z0-9\+\/]/g, "");
	var md = str.length % 4;
	var j, i, tmp;
	var octets = [];
	
	if(md) for(i = 0; i < 4 - md; i++) {str += "A";}   

	for(j = i = 0; i < str.length; i += 4, j += 3) {
		tmp = (b64decTable[str.charAt(i)] <<18) | 
					(b64decTable[str.charAt(i+1)] <<12) |
					(b64decTable[str.charAt(i+2)] << 6) | 
					b64decTable[str.charAt(i+3)];
					octets[j]    = tmp >>> 16;
					octets[j+1]	= (tmp >>> 8) & 0xff;
					octets[j+2]	= tmp & 0xff;
	}
	if(md) {octets.length -= [0,3,2,1][md];}
	return octets;
}

//	Base64 Strings to UTF8 Strings
//	@param	str		(String)Base64 strings
//	@return	(String)UTF8 strings
KRInsObj.Base64DecToString = function(str) {	
	return KRInsObj.OctetsToStr(KRInsObj.Base64Dec(str));
}

/////////////////////////////////////////
//	UTF8 strings to/from bytes Array

//	@param  str(String)	UTF8 strings
//	@return (Array) bytes Array
KRInsObj.StrToOctets = function(str) {
	var octets = [];
	var uriEncoded = encodeURIComponent(str);
	for(var i = 0, len = uriEncoded.length; i < len; i++) {
		var c = uriEncoded.charAt(i);
		var b;
		if (c == "%") {
			var hex = uriEncoded.charAt(++i) + uriEncoded.charAt(++i);
			b = parseInt(hex, 16);
		} else {
			b = c.charCodeAt(0);
		}
		octets.push(b);
	}
	return octets;
}

//	@param	octets(Array)	bytes Array
//	@return	(String) UTF8 strings
KRInsObj.OctetsToStr = function(octets) {
	var uriEncoded = [];
	for(var i = 0, len = octets.length; i < len; i++) {
		var hex = octets[i].toString(16);
		hex = ("0" + hex).substr(hex.length - 1, 2);
		uriEncoded.push("%" + hex);
	}
	return decodeURIComponent(uriEncoded.join(""));
}

//////////////////////////////////////////////////////////////////////////////////
//  MD5
if(!KRInsObj.MD5) {KRInsObj.MD5 = new Object();}

//	@param	octets	(Array)input data byte array
//	@reuturn	(Array)MD5 data byte array
KRInsObj.MD5.Hash = function(octets) {
  octets = KRInsObj.copyArray(octets);  //  prevent destroy source data.
	var len = octets.length * 8;
	// pad to nearest int.
	while (octets.length%4!=0) {
		octets[octets.length]=0;
	}
	// convert to an array of uint
	var a = [];
	for (var i=0;i<octets.length;i+=4) {
		var tmp = (octets[i+3] << 24) | (octets[i+2] << 16) | (octets[i+1] << 8) | octets[i];
		a.push(tmp);
	}
			
	var h = KRInsObj.MD5.core_md5(a, len);
			
	var out = [];
	for (i=0;i<4;i++) {
		out.push(h[i] & 0xff);
		out.push((h[i] >> 8) & 0xff);
		out.push((h[i] >> 16) & 0xff);
		out.push((h[i] >> 24) & 0xff);
	}
	return out;
}

//	@param	src	(String)UTF8 strings
//	@reuturn	(String)Base16 encoded MD5 string
KRInsObj.MD5.Base16HashFromString = function(src) {
	return KRInsObj.Base16Enc(KRInsObj.MD5.Hash(KRInsObj.StrToOctets(src)));
}

KRInsObj.MD5.core_md5 = function(x, len) {
  /* append padding */
  x[len >> 5] |= 0x80 << ((len) % 32);
  x[(((len + 64) >>> 9) << 4) + 14] = len;

  var a = 0x67452301; // 1732584193;
  var b = 0xEFCDAB89; //-271733879;
  var c = 0x98BADCFE; //-1732584194;
  var d = 0x10325476; // 271733878;

  for(var i = 0; i < x.length; i += 16)
  {
  	for(var j = 0; j < 16; j++) {
  		if(x[i+j]==undefined) {x[i+j]=0;}
  	}

    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;
		    
    a = KRInsObj.MD5.ff(a, b, c, d, x[i+ 0], 7 , 0xD76AA478);
    d = KRInsObj.MD5.ff(d, a, b, c, x[i+ 1], 12, 0xE8C7B756);
    c = KRInsObj.MD5.ff(c, d, a, b, x[i+ 2], 17, 0x242070DB);
    b = KRInsObj.MD5.ff(b, c, d, a, x[i+ 3], 22, 0xC1BDCEEE);
    a = KRInsObj.MD5.ff(a, b, c, d, x[i+ 4], 7 , 0xF57C0FAF);
    d = KRInsObj.MD5.ff(d, a, b, c, x[i+ 5], 12, 0x4787C62A);
    c = KRInsObj.MD5.ff(c, d, a, b, x[i+ 6], 17, 0xA8304613);
    b = KRInsObj.MD5.ff(b, c, d, a, x[i+ 7], 22, 0xFD469501);
    a = KRInsObj.MD5.ff(a, b, c, d, x[i+ 8], 7 , 0x698098D8);
    d = KRInsObj.MD5.ff(d, a, b, c, x[i+ 9], 12, 0x8B44F7AF);
    c = KRInsObj.MD5.ff(c, d, a, b, x[i+10], 17, 0xFFFF5BB1);
    b = KRInsObj.MD5.ff(b, c, d, a, x[i+11], 22, 0x895CD7BE);
    a = KRInsObj.MD5.ff(a, b, c, d, x[i+12], 7 , 0x6B901122);
    d = KRInsObj.MD5.ff(d, a, b, c, x[i+13], 12, 0xFD987193);
    c = KRInsObj.MD5.ff(c, d, a, b, x[i+14], 17, 0xA679438E);
    b = KRInsObj.MD5.ff(b, c, d, a, x[i+15], 22, 0x49B40821);

    a = KRInsObj.MD5.gg(a, b, c, d, x[i+ 1], 5 , 0xf61e2562);
    d = KRInsObj.MD5.gg(d, a, b, c, x[i+ 6], 9 , 0xc040b340);
    c = KRInsObj.MD5.gg(c, d, a, b, x[i+11], 14, 0x265e5a51);
    b = KRInsObj.MD5.gg(b, c, d, a, x[i+ 0], 20, 0xe9b6c7aa);
    a = KRInsObj.MD5.gg(a, b, c, d, x[i+ 5], 5 , 0xd62f105d);
    d = KRInsObj.MD5.gg(d, a, b, c, x[i+10], 9 ,  0x2441453);
    c = KRInsObj.MD5.gg(c, d, a, b, x[i+15], 14, 0xd8a1e681);
    b = KRInsObj.MD5.gg(b, c, d, a, x[i+ 4], 20, 0xe7d3fbc8);
    a = KRInsObj.MD5.gg(a, b, c, d, x[i+ 9], 5 , 0x21e1cde6);
    d = KRInsObj.MD5.gg(d, a, b, c, x[i+14], 9 , 0xc33707d6);
    c = KRInsObj.MD5.gg(c, d, a, b, x[i+ 3], 14, 0xf4d50d87);
    b = KRInsObj.MD5.gg(b, c, d, a, x[i+ 8], 20, 0x455a14ed);
    a = KRInsObj.MD5.gg(a, b, c, d, x[i+13], 5 , 0xa9e3e905);
    d = KRInsObj.MD5.gg(d, a, b, c, x[i+ 2], 9 , 0xfcefa3f8);
    c = KRInsObj.MD5.gg(c, d, a, b, x[i+ 7], 14, 0x676f02d9);
    b = KRInsObj.MD5.gg(b, c, d, a, x[i+12], 20, 0x8d2a4c8a);

    a = KRInsObj.MD5.hh(a, b, c, d, x[i+ 5], 4 , 0xfffa3942);
    d = KRInsObj.MD5.hh(d, a, b, c, x[i+ 8], 11, 0x8771f681);
    c = KRInsObj.MD5.hh(c, d, a, b, x[i+11], 16, 0x6d9d6122);
    b = KRInsObj.MD5.hh(b, c, d, a, x[i+14], 23, 0xfde5380c);
    a = KRInsObj.MD5.hh(a, b, c, d, x[i+ 1], 4 , 0xa4beea44);
    d = KRInsObj.MD5.hh(d, a, b, c, x[i+ 4], 11, 0x4bdecfa9);
    c = KRInsObj.MD5.hh(c, d, a, b, x[i+ 7], 16, 0xf6bb4b60);
    b = KRInsObj.MD5.hh(b, c, d, a, x[i+10], 23, 0xbebfbc70);
    a = KRInsObj.MD5.hh(a, b, c, d, x[i+13], 4 , 0x289b7ec6);
    d = KRInsObj.MD5.hh(d, a, b, c, x[i+ 0], 11, 0xeaa127fa);
    c = KRInsObj.MD5.hh(c, d, a, b, x[i+ 3], 16, 0xd4ef3085);
    b = KRInsObj.MD5.hh(b, c, d, a, x[i+ 6], 23,  0x4881d05);
    a = KRInsObj.MD5.hh(a, b, c, d, x[i+ 9], 4 , 0xd9d4d039);
    d = KRInsObj.MD5.hh(d, a, b, c, x[i+12], 11, 0xe6db99e5);
    c = KRInsObj.MD5.hh(c, d, a, b, x[i+15], 16, 0x1fa27cf8);
    b = KRInsObj.MD5.hh(b, c, d, a, x[i+ 2], 23, 0xc4ac5665);
		
    a = KRInsObj.MD5.ii(a, b, c, d, x[i+ 0], 6 , 0xf4292244);
    d = KRInsObj.MD5.ii(d, a, b, c, x[i+ 7], 10, 0x432aff97);
    c = KRInsObj.MD5.ii(c, d, a, b, x[i+14], 15, 0xab9423a7);
    b = KRInsObj.MD5.ii(b, c, d, a, x[i+ 5], 21, 0xfc93a039);
    a = KRInsObj.MD5.ii(a, b, c, d, x[i+12], 6 , 0x655b59c3);
    d = KRInsObj.MD5.ii(d, a, b, c, x[i+ 3], 10, 0x8f0ccc92);
    c = KRInsObj.MD5.ii(c, d, a, b, x[i+10], 15, 0xffeff47d);
    b = KRInsObj.MD5.ii(b, c, d, a, x[i+ 1], 21, 0x85845dd1);
    a = KRInsObj.MD5.ii(a, b, c, d, x[i+ 8], 6 , 0x6fa87e4f);
    d = KRInsObj.MD5.ii(d, a, b, c, x[i+15], 10, 0xfe2ce6e0);
    c = KRInsObj.MD5.ii(c, d, a, b, x[i+ 6], 15, 0xa3014314);
    b = KRInsObj.MD5.ii(b, c, d, a, x[i+13], 21, 0x4e0811a1);
    a = KRInsObj.MD5.ii(a, b, c, d, x[i+ 4], 6 , 0xf7537e82);
    d = KRInsObj.MD5.ii(d, a, b, c, x[i+11], 10, 0xbd3af235);
    c = KRInsObj.MD5.ii(c, d, a, b, x[i+ 2], 15, 0x2ad7d2bb);
    b = KRInsObj.MD5.ii(b, c, d, a, x[i+ 9], 21, 0xeb86d391);
		
		a += olda;
		b += oldb;
		c += oldc;
		d += oldd;
	}
  return [ a, b, c, d ];
}

/*
 * Bitwise rotate a 32-bit number to the left.
 */
KRInsObj.MD5.rol = function(num, cnt) {
  return (num << cnt) | (num >>> (32 - cnt));
}

/*
 * These functions implement the four basic operations the algorithm uses.
 */
KRInsObj.MD5.cmn = function(q, a, b, x, s, t) {
	return KRInsObj.MD5.safe_add(KRInsObj.MD5.rol(KRInsObj.MD5.safe_add(KRInsObj.MD5.safe_add(a, q), KRInsObj.MD5.safe_add(x, t)), s),b);
}
KRInsObj.MD5.ff = function(a, b, c, d, x, s, t) {
  return KRInsObj.MD5.cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
KRInsObj.MD5.gg = function(a, b, c, d, x, s, t) {
  return KRInsObj.MD5.cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
KRInsObj.MD5.hh = function(a, b, c, d, x, s, t) {
  return KRInsObj.MD5.cmn(b ^ c ^ d, a, b, x, s, t);
}
KRInsObj.MD5.ii = function(a, b, c, d, x, s, t) {
  return KRInsObj.MD5.cmn(c ^ (b | (~d)), a, b, x, s, t);
}
KRInsObj.MD5.safe_add = function(x, y) {
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

//////////////////////////////////////////////////////////////////////////////////
//  RC4
if(!KRInsObj.RC4) {KRInsObj.RC4 = new Object();}

// @param key     (Array)secret key byte array
// @param octets 	(Array)plain data byte array
// @return (Array)encrypted data byte array
KRInsObj.RC4.Encrypt = function(key, octets) {
	s = new Array();
	for (var i=0; i<256; i++) {
		s[i] = i;
	}
	var j = 0;
	var x;
	for (i=0; i<256; i++) {
		j = (j + s[i] + key[i % key.length]) % 256;
		x = s[i];
		s[i] = s[j];
		s[j] = x;
	}
	i = 0;
	j = 0;
	var ct = [];
	for (var y=0; y<octets.length; y++) {
		i = (i + 1) % 256;
		j = (j + s[i]) % 256;
		x = s[i];
		s[i] = s[j];
		s[j] = x;
		ct.push(octets[y] ^ s[(s[i] + s[j]) % 256]);
	}
	return ct;
}

// @param key     (Array)secret key byte array
// @param octets 	(Array)encrypted data byte array
// @return (Array)dectypted data byte array
KRInsObj.RC4.Decrypt = function(key, octets) {
	return KRInsObj.RC4.Encrypt(key,octets);
}

/////////////////////////////////////////
//	Keyring class methods

//	check KeyringFLASH client installed and do process.
//	params: {object}
//    script:	      HTML which written with secceeded.
//    target:       element id of target contents.
//		installPHP:		installation page url.
//		installImg:		image url for 'you need install.'
//		errorImg:			image url for 'your environment is not supported.'
KRInsObj.KeyringInstallTag = function(){
  var params = {};
  var script = null;
  //  initialize parameter
  // ---------------------------------------
  if(arguments.length < 1) {return;}
  //  in case KeyringInstallTag(script, params)
  if(typeof arguments[0] == "string") {
    script = arguments[0];
    if(typeof arguments[1] == "object") {params = arguments[1];}
  }
  //  in case KeyringInstallTag(params)
  else if(typeof arguments[0] == "object") {
    params = arguments[0];
    script = params.script;
  }
  else {return;}
  // ---------------------------------------
	var krins=new KRInsObj();
	var os=krins.getOS(); 
	var br=krins.getBrowser();
	var target = params.target ? document.getElementById(params.target) : null; 
  var target_display; //  original css display value of target
	//	default param
	if(params.installImg == null){params.installImg = 'http://www.keyring.net/js/install.jpg';}
	if(params.installPHP == null){params.installPHP = 'http://www.keyring.net/scripts/install2/install.php';}
	if(params.errorImg == null){params.errorImg = 'http://www.keyring.net/js/error.jpg';}
  //  hide target
  if(target) {
    target_display = target.style.display; 
    target.style.display = "none";
  }
	//	check
	if(krins.isKeyringClientSupported(2)){
		if(krins.getKeyringFlashClientVersion()){
			// Success
      if(target) {target.style.display = target_display;}
			if(script) {document.write(script);}
		}else{
			// not install KeyringFLASH
			var redirect_url = params.installPHP + "?type=3&successUrl=" + encodeURIComponent(location.href);
			if(br=="IE"){
				document.write('<input type="image" onClick="location.href(\'' + redirect_url + '\');" src="' + params.installImg + '" />');
			}else{
				document.write('<button type="button" onClick="location.href = \'' + redirect_url + '\';"><img src="' + params.installImg + '" /></button>');
			}
		}
	}else{
		document.write('<image src="' + params.errorImg + '"/>');
	}
}

//  control browser print
//	params: {object}
//    target:	   (string)target element id
//    msg:       (string)alternative message
KRInsObj.PrintControl = function(params) {
  var id_target_obj = params.target;
  var id_message_obj = "kr_" + KRInsObj.randomName(8);
  //  carete CSS
  var elem_ss1 = KRInsObj.createSytleElement('print'); 
	if(params.msg) {
    KRInsObj.addStyleRule(elem_ss1, "#" + id_message_obj, "display:block;");
  }
	KRInsObj.addStyleRule(elem_ss1, "#" + id_target_obj, "display:none;");
	if(params.msg) {
    var elem_ss2 = KRInsObj.createSytleElement('screen');
    KRInsObj.addStyleRule(elem_ss2, "#" + id_message_obj, "display:none;");
    
    KRInsObj.insertElement("div", {id:id_message_obj, align:"center"}, params.target, "after");
    KRInsObj.$(id_message_obj).innerHTML = params.msg;
  }
}

//  control screen capture plugin 
//  client supported:
//    (Windows)IE:1.11.3, Firefox:1.4.5
//    (Mac)Safari:1.3.7
//	params: {object}
//    target:	         (string)target element id
//    redirect_url:	    (string)redirect url for plugin ditected.
//    category:         (number, array)target plugins category.
//    time_slice:       (number)observation time slice.
KRInsObj.ScreenCapturePluginsControl = function(params) {
	
  var krins = new KRInsObj();
  var _os = krins.getOS();
  var _browser = krins.getBrowser();
  var _hp_url = location.protocol + "//" + (krins.isStaging() ? "wwwstg.keyring.net" : "www.keyring.net");
  var _bl_type = getType(_os, _browser);
  var _bl_name = "bl" + _bl_type;

  //  parameter
  if(params == undefined) {params = new Object()};
  if(!params.target) {throw new Error("\"target\" parameter not found.");}
  var _target = KRInsObj.$(params.target);
  var _redirect_url = params.redirect_url ? params.redirect_url : _hp_url + "/scripts/cb/pluginditect.php";
  var _categories = params.category ? params.category : [1];
  if(typeof(_categories) == "number") {
    _categories = new Array();
    _categories.push(params.category);
  }
  var _time_slice = params.time_slice ? params.time_slice : 3;
  
  //  load black list
  KRInsObj.createScriptElement(_hp_url + "/js/cb/bl1?" + (new Date().getTime()));
  KRInsObj.createScriptElement(_hp_url + "/js/cb/" + _bl_name + "?" + (new Date().getTime()));

  //  keyring pulgin instance
  KRInsObj.insertElement("div", {id:"krplugin"}, _target, "bottom");
  var krplugin = krins.getKRBrowserPlugin("krplugin");
  
  //  wait black list loading
  var wait_bl_timer = setInterval(function(){
      if(KRNS.bl1 && KRNS[_bl_name]) {
        clearInterval(wait_bl_timer);
      
        //  decryption
        eval("KRNS.bl1 = " + decryptStrings(KRNS.bl1));
        eval("KRNS[_bl_name] = " + decryptStrings(KRNS[_bl_name]));
        
        //  start observer
        checkPlugins(_categories, _bl_type, KRNS.bl1, KRNS[_bl_name], _redirect_url);
        if(_time_slice > 0) {
          setInterval(function(){
            checkPlugins(_categories, _bl_type, KRNS.bl1, KRNS[_bl_name], _redirect_url);
          }, _time_slice * 1000);
        }  
      }
    }, 50);

  //  create black list url path
  //  @param  os        (string)OS
  //  @param  browser   (string)Browser
  //  @return (Number)type value.
  function getType(os, browser) {
    var _type = 0;
    if(os == "Windows" && browser == "IE") {  
      _type = 2;
    }
    else if(browser == "Firefox") {
      _type = 4;
    }
    else if(browser == "Safari") {
      _type = 3;
    }
    else {
      throw new Error("os(" + os + "), browser(" + browser + ") is not supported.");
    }
    return _type;
  }
    
  //  check a plugin ditect in the black list
  //  @param  categories    (array)target category
  //  @param  type          (number)black list type
  //  @param  plugin        (object)target plugin object
  //  @param  black_list    (array)black list
  //  @return (number)-1: not found, other: founded index
  function findPluginOnBlackList(categories, bl_type, plugin, black_list) {
    for(var i = 0; i < black_list.length; i++) {
      //  category check
      if(KRInsObj.searchArray(categories, black_list[i].category) == -1) {continue;}
      //  plugin check
      switch(bl_type) {
      //  navigator.plugins
      case 1:
        if(black_list[i].name && plugin.name && (plugin.name == black_list[i].name)) {return i;}
        if(black_list[i].filename && plugin.filename && (plugin.filename == black_list[i].filename)) {return i;}
        break;
      //  Windows and IE
      case 2:
        if(black_list[i].filename && plugin.filename && (plugin.filename == black_list[i].filename)) {return i;}
        if(black_list[i].org_filename && plugin.org_filename && (plugin.org_filename == black_list[i].org_filename)) {return i;}
        if(black_list[i].product_name && plugin.product_name && (plugin.product_name == black_list[i].product_name)) {return i;}
        break;
      //  Safari
      case 3:
        //  skip disabled plugin
        if(plugin.enabled == false) {return -1;}
        //  check
        if(black_list[i].archive_name && plugin.archive_name && isMatchedPlugin(plugin.archive_name, black_list[i].archive_name, ".safariextz")) {return i;}
        if(black_list[i].bundle_name && plugin.bundle_name && isMatchedPlugin(plugin.bundle_name, black_list[i].bundle_name, ".safariextension")) {return i;}
        break;
      //  Firefox
      case 4:
        //  skip disabled plugin
        if(plugin.app_disabled == true || plugin.user_disabled == true) {return -1;}
        //  check
        if(black_list[i].id && plugin.id && (plugin.id == black_list[i].id)) {return i;}
        break;
      }
    }
    return -1;
  }
  
  //  check plugin is matching to black list plugin
  //  * check various name ex:"plugin_name-1.safariextz"
  //  @return true:matched, false:not matched
  function isMatchedPlugin(plugin_name, black_list_name, extension) {
    var regexp = new RegExp(black_list_name.substr(0, black_list_name.indexOf(extension)) + "-?.*\\" + extension);
    return (plugin_name.search(regexp) == 0) ? true : false;
  }
  
  //  redirect 
  function redirectPage(redirect_url, type, info_name, info_desc, org_url) {
    if(redirect_url == "alert") {alert("plugin ditected: " + info_name); return;}
    var query = "type=" + type + "&name=" + encodeURIComponent(info_name) + 
      "&desc=" + encodeURIComponent(info_desc) +
      "&ret_url=" + encodeURIComponent(org_url);
    redirect_url += (redirect_url.indexOf("?") == -1) ? "?" + query : "&" + query;
    location.href = redirect_url;
  }
    
  //  check plugin
  //  @param  categories    (array)target category
  //  @param  type          (number)black list type
  //  @param  black_list1   (object)common black list
  //  @param  black_list2   (object)browser dependent black list
  //  @param  redirect_url  (string)redirect url
  function checkPlugins(categories, bl_type, black_list1, black_list2, redirect_url) {
    //  check navigator.plugins
    var plugins1 = navigator.plugins;
    for(var i = 0; i < plugins1.length; i++) {
      var index = findPluginOnBlackList(categories, 1, plugins1[i], black_list1.list);
      if(index != -1) {
        var obj = black_list1.list[index];
        redirectPage(redirect_url, 1, obj.info_name, obj.info_desc, location.href);
        return;
      }
    }

    //  check loaded addons
    //  * skip if old version plugin using
    if(typeof(krplugin.getPlugins) == "unknown" || typeof(krplugin.getPlugins) == "function") {      
      eval("var plugins2 = " + krplugin.getPlugins(""));
      //  skip if extensions disabled(Safari only)
      if(plugins2.extensions_enabled == false) {return;}
      //  check loading plugins
      for(var i = 0; i < plugins2.list.length; i++) {
        var index = findPluginOnBlackList(categories, bl_type, plugins2.list[i], black_list2.list);
        if(index != -1) {
          var obj = black_list2.list[index];
          redirectPage(redirect_url, bl_type, obj.info_name, obj.info_desc, location.href);
          return;
        }
      }
    }
  
    return;
  }
   
  //  decrypt list data
  function decryptStrings(src) {
    var rand_data = KRInsObj.Base64Dec(src.substr(0, 24));
    var key = KRInsObj.MD5.Hash(rand_data);
    var src_data = KRInsObj.Base64Dec(src.substr(24));
    var dec_data = KRInsObj.RC4.Decrypt(key, src_data);
    return KRInsObj.OctetsToStr(dec_data);
  }

}



