// <script type="text/javascript"> Diese Datei ist utf8

	function updmail(o,m) {
		o.setAttribute('href','mailto:'+nocrypt(m));
		return true;
	}

	function nocrypt(text) {
		var code='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!/$%&()=?-:_.,;#+*@';
		var tl=text.length;
		var rand=tl;

		var code2=[];

		var cl=code.length;
		while(cl>0) {
			rand=rand%cl;
			var char=code.charAt(rand);
			rand+=char.charCodeAt(0);
			code2.push(char);
			code=code.replace(char,'');
			cl=code.length;
		}

		code2=code2.join('');

		cl=code2.length-1;
		var ret=[];
		for(var i=0;i<tl;i++) {
			var p=code2.indexOf(text.charAt(i));
			if(p==-1) ret.push(text.charAt(i));
			else ret.push(code2.charAt(cl-p));
		}

		return ret.join('');
	}

	function slidesload() {
		pics++;
		if(pics<slides.length)  return true;

		$('#slideshow img').remove();
		var hd=$('#slideshow');
		
		for(var i=0;i<slides.length;i++) hd.append(slides[i].img);
		hd.cycle({fx: 'fade', sync: 1, timeout:6000, speed:1000, after:slidesinfo, before:slidesinfooff});
	}

	function slidesinfooff(currSlideElement, nextSlideElement, options, forwardFlag) {
			$('#slideshowinfo').css('display','none');
	}


	function slidesinfo(currSlideElement, nextSlideElement, options, forwardFlag) {
		slide=nextSlideElement.slideindex;
		if(slides[slide] && slides[slide].info!='') {
			$('#slideshowinfo').html(slides[slide].info).css('display','block');
		}
	}


	var Core={
		plugins:{},

		Init: function() {
		},

		loadPlugin:function(name) {
			if(Core.plugins[name]==true) return;
			var scr=document.createElement('script');
			var h=document.getElementsByTagName('head')[0];
			scr.type='text/javascript';
			scr.src=home+'javascript/'+name.toLowerCase()+'.js';
//			scr.setAttribute('onload','Core._plugInit(\''+name+'\')');
			h.appendChild(scr);
		},

		_plugInit:function (name) {
			eval(name+'_Init()');
			Core.plugins[name]=true;
		},

		getX: function(e) {
			var x=0;
			while(e) {
				x+=e.offsetLeft;
				e=e.offsetParent;
			}
			return x;
		},

		getY: function(e) {
			var y=0;
			while(e) {
				y+=e.offsetTop
				e=e.offsetParent;
			}
			return y;
		},

		checkDate: function(date) {
			// jjjj-m-d
			var r1=/^(20[012][0-9])-([0-9]|0[0-9]|1[012])-([0-9]|[012][0-9]|3[01])$/;
			// jjjjmmdd
			var r2=/^(20[012][0-9])(-|)(0[0-9]|1[012])(-|)([012][0-9]|3[01])$/;
			// d.m.jjjj
			var r3=/^([0-9]|[012][0-9]|3[013])\.([0-9]|[012][0-9]|3[01])\.(20[012][0-9])$/;
			// m/d/jjjj
			var r4=/^([0-9]|[012][0-9]|3[01])\/([0-9]|[012][0-9]|3[013])\/(20[012][0-9])$/;

			ret=new Date(0);

			if(res=date.match(r1)){
				ret.setYear(res[1]);
				ret.setMonth(res[2]);
				ret.setDate(res[3]);
				return ret;
			}
			else if(res=date.match(r2)){
				ret.setYear(res[1]);
				ret.setMonth(res[3]);
				ret.setDate(res[5]);
				return ret;
			}
			else if(res=date.match(r3)){
				ret.setYear(res[3]);
				ret.setMonth(res[2]);
				ret.setDate(res[1]);
				return ret;
			}
			else if(res=date.match(r4)){
				ret.setYear(res[3]);
				ret.setMonth(res[1]);
				ret.setDate(res[2]);
				return ret;
			}
			return false;
		},

		indicatorOn: function(n) {
			var e=document.getElementsByName(n);
			for(i=0;i<e.length;i++) {
				e[i].className='indicatoron';
			}
		},

		indicatorOff: function(n) {
			var e=document.getElementsByName(n);
			for(i=0;i<e.length;i++) {
				e[i].className='indicatoroff';

			}
		}
	}

	Core.Init();


	Core.XML={
		agent:null,
		timeout:5000,

		newXML: function() {
			if(XMLHttpRequest) return new XMLHttpRequest();
			return null; // IE wird im weiteren nicht unterstüzt, dann wird der rest nicht benötigt
			/*
			try { return ActiveXObject("Msxml2.XMLHTTP");}
			catch (e){}
			try { return ActiveXObject("Microsoft.XMLHTTP");}
			catch (e){}
			return null;
			*/
		},

		setAgent:function(a) {
			if(a=='') a=null;
			this.agent=a;
		},

		setTimeout: function(s) {
			if(s<20) s=s*1000;
			this.timeout=s;
		},

		get: function(url) {
			function requestTimeout(){
				xml.abort();
			}

			var timer=window.setTimeout(requestTimeout,this.timeout);
			var xml=this.newXML();
			xml.open('get',url,false);
			xml.setRequestHeader('X_RPC_ID','1');
			xml.setRequestHeader('Content-Type','text/plain; charset=utf-8');
			xml.send(null);

			if(xml.readyState==4){
				window.clearTimeout(timer);
				return this._defaultReadyState(xml);
			}
			throw {src:'Server',msg:'readyState !=4',state:xml.readyState};
		},

		post: function(url,data) {
			function requestTimeout(){
				xml.abort();
			}

			var timer=window.setTimeout(requestTimeout,this.timeout);
			var xml=this.newXML();
			xml.open('post',url,false);
			xml.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=utf-8');
			xml.setRequestHeader('X_RPC_ID','1');
			data=this._encodeData(data);
			xml.send(data);

			if(xml.readyState==4){
				window.clearTimeout(timer);
				return this._defaultReadyState(xml);
			}
			throw {src:'Server',msg:'readyState !=4',state:xml.readyState};
		},

		postform: function(url,form) {
			if(form==null) throw {src:'XMLRequest',msg:'argument "form" is null'};
			if(form.tagName!='FORM') throw {src:'XMLRequest',msg:'argument "form" is not a Form'};

			var data={};
			for(var i=0;i<form.elements.length;i++) {
				if(form.elements[i].tagName!='INPUT' && form.elements[i].tagName!='SELECT' && form.elements[i].tagName!='TEXTAREA') continue; 
				data[form.elements[i].name]=form.elements[i].value;
			}
			return this.post(url,data);
		},

		asyncGet: function(url,callback) {
			if(typeof callback !='function') throw {src:'XMLRequest',msg:'callback not a function'};
			function requestTimeout(){
				xml.abort();
				callback(false);
			}

			var timer=window.setTimeout(requestTimeout,this.timeout);
			var xml=this.newXML();
			xml.open('get',url,true);
			xml.onreadystatechange= function() {
				if(xml.readyState==4){
					window.clearTimeout(timer);
					var result=Core.XML._defaultReadyState(xml);
					callback(result);
					xml=null;
				}
			}
			xml.send(null);
		},

		asyncPost: function(url,data,callback) {
			if(typeof callback !='function') throw {src:'XMLRequest',msg:'callback not a function'};
			function requestTimeout(){
				xml.abort();
				callback(false);
			}

			var timer=window.setTimeout(requestTimeout,this.timeout);
			var xml=this.newXML();
			xml.open('post',url,true);
			xml.onreadystatechange= function() {
				if(xml.readyState==4){
					window.clearTimeout(timer);
					var result=Core.XML._defaultReadyState(xml);
					callback(result);
					xml=null;
				}
			}
			xml.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=utf-8');
			data=this._encodeData(data);
			xml.send(data);
		},

		_encodeData: function(data) {
			var r=/%20/;
			var ret=[];
			for(i in data) {
				n=encodeURIComponent(i).replace(r,'+');
				v=encodeURIComponent(data[i]).replace(r,'+');
				ret.push(n+'='+v);
			}
			return ret.join('&');
		},

		_defaultReadyState: function(xml) {
			if(xml.status!=200) throw {src:'Server',msg:'Server status !=200',status:xml.status};
			ct=xml.getResponseHeader('Content-Type');
			ct=ct.split(';');
			var ret;
			switch(ct[0].toLowerCase()) {
				case 'application/json':
				case 'text/javascript':
				case 'text/json':
					return eval('('+xml.responseText+')');

				case 'text/xml':
					return xml.responseXml;

				case 'text/plain':
				default: return xml.responseText;
			}
			return false;
		}
	}

	Core.highlight={
		current:null,
		closebutton:null,
		cache:[],

		Init: function() {
			$(document).ready(function() {
				$('.piczoom img').map(function(){
					$(this).attr('dst',$(this).parent().attr('href'));
					$(this).click(Core.highlight.doHighlight);
				});
			});

			$(document).scroll(Core.highlight.doResize);

		},

		doResize: function() {
			if(!$('#highlight')) return true;

			var px=$(window).scrollLeft();
			var py=$(window).scrollTop();
			var frame=$('#highlight_frame');
			var img=$('#highlight_img');

			$('#highlight').css({top:py+'px',left:px+'px',width:$('body').innerWidth()-1+'px',height:$('body').innerHeight()-1+'px'});

			px=0;
			py=0;

			if(!img || !frame) return false;

			var wiw=$(window).width()*0.9;
			var wih=$(window).height()*0.9;
			var nh=parseInt(img.attr('height'));
			var nw=parseInt(img.attr('width'));

			if(wih>nh || wiw>nw) {
				nw=parseInt(img.attr('owidth'));
				nh=parseInt(img.attr('oheight'));
			}

			if(nw>wiw) {
				nh=nh/nw*wiw;
				nw=wiw;
			}

			if(nh>wih) {
				nw=nw/nh*wih;
				nh=wih;
			}

			var wiw=$(window).width();
			var wih=$(window).height();

			frame.css({width:nw+'px',height:nh+'px',left:(wiw-nw)/2+px+'px',top:(wih-nh)/2+py+'px'});
			img.attr('width',nw);
			img.attr('height',nh);

			$('#highlight_indi').css({left:wiw/2+'px',top:wih/2+'px'});
			$('#highlight_info').css({left:(wiw-nw)/2+px+'px',top:(wih-nh)/2+nh+py+'px'});
			$('#highlight_org').css({left:(wiw-nw)/2+px+'px',top:(wih-nh)/2+nh+py+'px'});
			$('#highlight_close').css({left:(wiw-nw)/2+nw+px+'px',top:(wih-nh)/2+nh+py+'px'});
		},

		doClose: function(e) {
			$('#highlight').remove();
			return false;
		},

		doHighlight: function(event) {
			$('#highlight').remove();

			// 1. sieb anlegen
			var temp=$('<div>');
			temp.attr('id','highlight');
			$('body').append(temp);


			// 2. den rahmen für das bild
			var frame=$('<div>');
			frame.attr('id','highlight_frame');
			$('#highlight').append(frame);


			// 3. kopie des bildes welches angeclickt wurde
			var img=$('<img>');
			img.attr('id','highlight_img')
				.attr('src',$(this).attr('src'))
				.attr('alt',$(this).attr('alt'))
				.attr('owidth',$(this).attr('width'))
				.attr('oheight',$(this).attr('height'));
			frame.append(img);

			$('#highlight_img').click(Core.highlight.doClose);

			// 4. aktivitäts indikator erzeugen
			var indi=$('<div>');
			indi.attr('id','highlight_indi');
			$('#highlight').append(indi);


			// 5. info button
			var info=$('<div>');
			info.attr('id','highlight_info');
			$('#highlight').append(info);

			var infoa=$('<a>');
			infoa.attr('href',$(this).parent().attr('href'))
				.attr('title',$(this).attr('title'))
				.append(document.createTextNode(' ')); // das ist ein nbsp und kein space!
			info.append(infoa);

			// 6. schließen button
			var close=$('<div>');
			close.attr('id','highlight_close');
			$('#highlight').append(close);

			var closea=$('<a>');
			closea.click(Core.highlight.doClose)
					.attr('href','#')
					.attr('title','Schließen')
					.append(document.createTextNode(' ')); // das ist ein nbsp und kein space!
			close.append(closea);

			var picname=$(this).parent().attr('href').replace(home,'');

			// alles ordentlich ausrichten
			Core.highlight.doResize();

			// wir benötigen eine eindeutige nummer um zu erkennen ob wir das richtige bild laden
			// es könnten ganz schnelle leute schaffen ein bild zu öffnen und zu schließen bevor überhaupt díe antwort zurück ist
			x=Math.random();
			$('#highlight').attr('rand',x);
			try {
				// bild info holen für ein bild das maximal 90% der aktuellen möglichen fläche hat
				$.getJSON(home+'piczoom/file-'+Base64.encode(picname)+'/x-'+Math.round(($('body').innerWidth()*0.9),0)+'/y-'+Math.round($('body').innerHeight()*0.9,0),function (data) {

					// 7. neuen node für das gesuchte bild erstellen
					var img2=new Image();
					img2.onload=function(){
						// fals zwischen durch das bild zu gemacht wurde und/oder ein anderes geöffnet wurde
						if($('#highlight').attr('rand')!=x) return false;

						$('#highlight_img')
							.attr('owidth',img2.width)
							.attr('oheight',img2.height)
							.attr('src',img2.src);
						$('#highlight_indi').remove();
						Core.highlight.doResize();
					}
					// 8. bild laden
					img2.src=data.file;

					// 5b. "orginal" button
					var org=$('<div>');
					org.attr('id','highlight_org');
					$('#highlight').append(org);

					var orga=$('<a>');
					orga.attr('href',data.ofile)
						.attr('title','Orginal Bild')
						.append(document.createTextNode(' ')); // das ist ein nbsp und kein space!
					org.append(orga);

				});
			}
			catch(e) {
				alert('Fehler beim laden des bergößerten Bildes');
				return false;
			}

			event.preventDefault();
			return false;
		}
	}
	Core.highlight.Init();



/**
*
*  Base64 encode / decode
*  http://www.webtoolkit.info/
*
**/
 
var Base64 = {
 
	// private property
	_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_*", // Base64 URL sicher
 
	// 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;
	}
}


/*
 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
 * in FIPS 180-1
 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for details.
 */

/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */

/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_sha1(s)    { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); }
function b64_sha1(s)    { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); }
function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); }
function hex_hmac_sha1(k, d)
  { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
function b64_hmac_sha1(k, d)
  { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
function any_hmac_sha1(k, d, e)
  { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); }

/*
 * Perform a simple self-test to see if the VM is working
 */
function sha1_vm_test()
{
  return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d";
}

/*
 * Calculate the SHA1 of a raw string
 */
function rstr_sha1(s)
{
  return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8));
}

/*
 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
 */
function rstr_hmac_sha1(key, data)
{
  var bkey = rstr2binb(key);
  if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8);

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
  return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160));
}

/*
 * Convert a raw string to a hex string
 */
function rstr2hex(input)
{
  try { hexcase } catch(e) { hexcase=0; }
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var output = "";
  var x;
  for(var i = 0; i < input.length; i++)
  {
    x = input.charCodeAt(i);
    output += hex_tab.charAt((x >>> 4) & 0x0F)
           +  hex_tab.charAt( x        & 0x0F);
  }
  return output;
}

/*
 * Convert a raw string to a base-64 string
 */
function rstr2b64(input)
{
  try { b64pad } catch(e) { b64pad=''; }
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var output = "";
  var len = input.length;
  for(var i = 0; i < len; i += 3)
  {
    var triplet = (input.charCodeAt(i) << 16)
                | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
                | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > input.length * 8) output += b64pad;
      else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
    }
  }
  return output;
}

/*
 * Convert a raw string to an arbitrary string encoding
 */
function rstr2any(input, encoding)
{
  var divisor = encoding.length;
  var remainders = Array();
  var i, q, x, quotient;

  /* Convert to an array of 16-bit big-endian values, forming the dividend */
  var dividend = Array(Math.ceil(input.length / 2));
  for(i = 0; i < dividend.length; i++)
  {
    dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
  }

  /*
   * Repeatedly perform a long division. The binary array forms the dividend,
   * the length of the encoding is the divisor. Once computed, the quotient
   * forms the dividend for the next step. We stop when the dividend is zero.
   * All remainders are stored for later use.
   */
  while(dividend.length > 0)
  {
    quotient = Array();
    x = 0;
    for(i = 0; i < dividend.length; i++)
    {
      x = (x << 16) + dividend[i];
      q = Math.floor(x / divisor);
      x -= q * divisor;
      if(quotient.length > 0 || q > 0)
        quotient[quotient.length] = q;
    }
    remainders[remainders.length] = x;
    dividend = quotient;
  }

  /* Convert the remainders to the output string */
  var output = "";
  for(i = remainders.length - 1; i >= 0; i--)
    output += encoding.charAt(remainders[i]);

  /* Append leading zero equivalents */
  var full_length = Math.ceil(input.length * 8 /
                                    (Math.log(encoding.length) / Math.log(2)))
  for(i = output.length; i < full_length; i++)
    output = encoding[0] + output;

  return output;
}

/*
 * Encode a string as utf-8.
 * For efficiency, this assumes the input is valid utf-16.
 */
function str2rstr_utf8(input)
{
  var output = "";
  var i = -1;
  var x, y;

  while(++i < input.length)
  {
    /* Decode utf-16 surrogate pairs */
    x = input.charCodeAt(i);
    y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
    if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
    {
      x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
      i++;
    }

    /* Encode output as utf-8 */
    if(x <= 0x7F)
      output += String.fromCharCode(x);
    else if(x <= 0x7FF)
      output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
                                    0x80 | ( x         & 0x3F));
    else if(x <= 0xFFFF)
      output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
                                    0x80 | ((x >>> 6 ) & 0x3F),
                                    0x80 | ( x         & 0x3F));
    else if(x <= 0x1FFFFF)
      output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
                                    0x80 | ((x >>> 12) & 0x3F),
                                    0x80 | ((x >>> 6 ) & 0x3F),
                                    0x80 | ( x         & 0x3F));
  }
  return output;
}

/*
 * Encode a string as utf-16
 */
function str2rstr_utf16le(input)
{
  var output = "";
  for(var i = 0; i < input.length; i++)
    output += String.fromCharCode( input.charCodeAt(i)        & 0xFF,
                                  (input.charCodeAt(i) >>> 8) & 0xFF);
  return output;
}

function str2rstr_utf16be(input)
{
  var output = "";
  for(var i = 0; i < input.length; i++)
    output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
                                   input.charCodeAt(i)        & 0xFF);
  return output;
}

/*
 * Convert a raw string to an array of big-endian words
 * Characters >255 have their high-byte silently ignored.
 */
function rstr2binb(input)
{
  var output = Array(input.length >> 2);
  for(var i = 0; i < output.length; i++)
    output[i] = 0;
  for(var i = 0; i < input.length * 8; i += 8)
    output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
  return output;
}

/*
 * Convert an array of big-endian words to a string
 */
function binb2rstr(input)
{
  var output = "";
  for(var i = 0; i < input.length * 32; i += 8)
    output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
  return output;
}

/*
 * Calculate the SHA-1 of an array of big-endian words, and a bit length
 */
function binb_sha1(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << (24 - len % 32);
  x[((len + 64 >> 9) << 4) + 15] = len;

  var w = Array(80);
  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;
  var e = -1009589776;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;
    var olde = e;

    for(var j = 0; j < 80; j++)
    {
      if(j < 16) w[j] = x[i + j];
      else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
      var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
                       safe_add(safe_add(e, w[j]), sha1_kt(j)));
      e = d;
      d = c;
      c = bit_rol(b, 30);
      b = a;
      a = t;
    }

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
    e = safe_add(e, olde);
  }
  return Array(a, b, c, d, e);

}

/*
 * Perform the appropriate triplet combination function for the current
 * iteration
 */
function sha1_ft(t, b, c, d)
{
  if(t < 20) return (b & c) | ((~b) & d);
  if(t < 40) return b ^ c ^ d;
  if(t < 60) return (b & c) | (b & d) | (c & d);
  return b ^ c ^ d;
}

/*
 * Determine the appropriate additive constant for the current iteration
 */
function sha1_kt(t)
{
  return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
         (t < 60) ? -1894007588 : -899497514;
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

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

