/**
 * Knowledge Square JavaScript framework & Library
 * (c) 2009 K2 Corporation
 */

var K2 = {
	name: 'Knowledge Square JavaScript Framework & Library',
	version: '0.8.080630',
	author: 'Min-Lung Lee',
	base: 'mootools 1.2'
};

// ** Common Data ***********************************
K2.Icon = {
	loading: 'images/ico/loading_16.gif',
	close: 'images/ico/close.gif',
	dialog: 'images/ico/dlg_16.gif',
	success16: 'images/ico/success_16.gif',
	success50: 'images/ico/success_50.gif',
	info16: 'images/ico/info_16.gif',
	info50: 'images/ico/info_50.gif',
	alert16: 'images/ico/alert_16.gif',
	alert50: 'images/ico/alert_50.gif',
	error16: 'images/ico/error_16.gif',
	error50: 'images/ico/error_50.gif'
}

// ** Common Util Function *****************************
K2.callOnLoad = function(init){
	window.addEvent('domready', init);
}

// callback function for native JavaScript array sorting method ([].sort()) to sort object
// binds 2 arguments 'property' & 'sort type(asc or desc)'
K2.comparator = function(o1, o2){
	if($type(o1) == 'number') return o1 - o2;
	else if($type(o1) == 'string'){
		var a = o1.toLowerCase();
		var b = o2.toLowerCase();
		return a < b ? -1 : (a > b ? 1 : 0);
	}
	else if($type(o1) == 'object'){
		if(!$defined(this[0])){
			alert(K2.name + ': comparator need a property to compare');
			return 0;
		}
		var prop = this[0];
		var isAsc = !$defined(this[1]) ? true : this[1] == 'asc';
		if($type(o1[prop]) == 'number')
			return isAsc ? o1[prop] - o2[prop] : o2[prop] - o1[prop];
		else if($type(o1[prop]) == 'string'){
			var a = o1[prop].toLowerCase();
			var b = o2[prop].toLowerCase();
			return isAsc ? (a < b ? -1 : a > b ? 1 : 0) : (a < b ? 1 : a > b ? -1 : 0);
		}
		else return 0;
	}
	else return 0;
}

K2._center = function(obj){
	var html = document.getElementsByTagName('html')[0];
	var x0 = html.clientWidth, y0 = html.clientHeight;
	var x = (x0 - obj.scrollWidth) / 2;
	var y = (y0 - ((obj.scrollHeight < 200)? 200: obj.scrollHeight)) / 2;
	obj.style.left = (x < 0? 0 : x) + 'px';
	obj.style.top = (y < 0? 0 : y) + html.scrollTop + 'px';
}

K2._getBgColor = function(obj){
	obj = $(obj);
	var bg = obj.getStyle('background-color');
	return bg != 'transparent' ? bg : K2._getBgColor(obj.getParent());
}

// ** 01. Group (extend from mootools.net's Group ) **************
K2.Group = new Class({
	Extends: Group,
	name: K2.name + '(K2.Group)',
	add: function(obj){ this.instances.extend(new Array(obj)); },
	size: function(){ return this.instances.length; }
});

// ** 02. Dialog *****************************************
K2.Dialog = new Class({
	name: K2.name + '(K2.Dialog)',

	initialize: function(options){
		this.msgTypes = ['success', 'info', 'alert', 'error'];
		this.types = ['normal', 'loading'];
		this.dialogs = new Hash();
		this.dialogs[this.msgTypes[0]] = this._genMsgDialog(this.msgTypes[0], '#C4EFB4', '#66D53D', K2.Icon.success50);
		this.dialogs[this.msgTypes[1]] = this._genMsgDialog(this.msgTypes[1], '#CADAF4', '#76A0E5', K2.Icon.info50);
		this.dialogs[this.msgTypes[2]] = this._genMsgDialog(this.msgTypes[2], '#FDF8CA', '#F4DD45', K2.Icon.alert50);
		this.dialogs[this.msgTypes[3]] = this._genMsgDialog(this.msgTypes[3], '#FFBBBB', '#DC2727', K2.Icon.error50);
		this.dialogs[this.types[0]] = this._genNormalDialog();
		this.dialogs[this.types[1]] = this._genLoadingDialog();
		window.onresize = this._adjustmentMasks.bindWithEvent(this);
		this.options = $merge({

		}, options);
	},

	openMsgDialog: function(title, content, type){
		if(!this.msgTypes.contains(type))
			type = this.msgTypes[0];
		this._openModalMsgDialog(title, content, this.dialogs[type]);
	},

	_openModalMsgDialog: function(title, content, dlg){
		dlg.getElement('span[class=msg-title]').set('text', title);
		dlg.getElement('span[class=msg-txt]').set('text', content);
		this._openDialog(dlg);
	},

	openNormalDialog: function(title, text){
		var d = this.dialogs[this.types[0]];
		if(title && title != "")
			d.getElement('span[class=normal-title]').set('text', title);
		if(text && text != "")
			d.getElement('span[class=normal-txt]').set('text', text);
		this._openDialog(d);
	},

	openLoadingDialog: function(text){
		var d = this.dialogs[this.types[1]];
		if(text && text != "")
			d.getElement('span[class=loading-txt]').set('text', text);
		this._openDialog(d);
	},

	closeLoadingDialog: function(){
		this._closeDialog(null, this.types[1]);
	},

	_genMsgDialog: function(type, color1, color2, icon){
		var outer = '<table border="0" cellpadding="0" cellspacing="3" bgcolor="#FFFFFF" style="border:1px solid #808080">' +
			'<tr><td style="border:1px dotted #808080">XXX</td></tr></table>';

		var div = document.createElement('div');
		div.style.position = 'absolute';
		var dialog = outer.replace('XXX',
			'<table class="m" width="360" border="0" cellpadding="5" cellspacing="3" bgcolor="' + color1 + '" style="border-top:3px solid ' + color2 + '">' +
			'<tr><th width="10%"><img class="icon" src="' + icon + '" style="cursor:move"></th>' +
			'<td width="90%"><a href="javascript:void(0)"><img class="closeIcon" src="' + K2.Icon.close + '" align="right" border="0"></a>' +
			'<span class="msg-title"></span><br><span class="msg-txt"></span></td></tr></table>');
		div.innerHTML = dialog;
		var closeIcon = $(div).getElement('img[class=closeIcon]');
		closeIcon.onclick = this._closeDialog.bindWithEvent(this, type);
		document.body.appendChild(div);

		$(div).fade('hide');
		$(div).makeDraggable({handle: $(div).getElement('img[class=icon]')});

		return div;
	},

	_genNormalDialog: function(){
		var normal = '<table width="360" border="0" align="center" cellpadding="8" cellspacing="3" bgcolor="#FFFFFF" style="border:1px solid #808080">' +
			'<tr><td bgcolor="#E9E6DE" style="border:1px dotted #808080">' +
			'<img class="closeIcon" src="images/ico/close.gif" width="16" height="16" align="right" />' +
			'<span class="normal-title" style="font-weight:bold"></span><br><span class="normal-txt"></span></td></tr></table>';

		var div = document.createElement('div');
		div.style.position = 'absolute';
		div.innerHTML = normal;
		var closeIcon = $(div).getElement('img[class=closeIcon]');
		closeIcon.onclick = this._closeDialog.bindWithEvent(this, this.types[0]);
		document.body.appendChild(div);

		$(div).fade('hide');
		$(div).makeDraggable();

		return div;
	},

	_genLoadingDialog: function(){
		var outer = '<table border="0" cellpadding="0" cellspacing="3" bgcolor="#FFFFFF" style="border:1px solid #808080">' +
			'<tr><td style="border:1px dotted #808080;cursor:move">XXX</td></tr></table>';
		var div = document.createElement('div');
		div.style.position = 'absolute';
		var dialog = outer.replace('XXX',
			'<table class="m" border="0" cellspacing="3" bgcolor="#E0E0E0"><tr><td style="padding:10px 30px 10px 5px">' +
			'<img src="' + K2.Icon.loading + '" hspace="10" align="absmiddle"><span class="loading-txt">Loading</span></td></tr></table>');
		div.innerHTML = dialog;
		document.body.appendChild(div);

		$(div).fade('hide');
		$(div).makeDraggable();

		return div;
	},

	// general dialogs ...
	addDialog: function(dialogId, options){
		if(this.msgTypes.indexOf(dialogId.toLowerCase()) > 0){
			alert(this.name + ":\nCan not use reserve word: '" + dialogId);
			return;
		}
		var dialog = $(dialogId);
		if(!dialog){
			alert(this.name + ':\nCan not find DOM object: ' + dialogId);
			return;
		}
		options = $merge({title: "104"}, options);
		this.dialogs[dialogId] = this._genDialog(dialog, options);
	},

	openDialog: function(dialogId, options){
		this._openDialog(this.dialogs[dialogId], options);
	},

	_openDialog: function(d, options){
		if(!d){
			alert(this.name + ':\n"' + dialogId + '" does not add yet!');
			return;
		}
		K2._center(d);
		options = options || {modal: true};
		d.style.zIndex = options.modal ? Number(this._showMask()) + 1 : 100 * this._getMaskCount() + 3;
		d.fade('in');
	},

	closeDialog: function(dialogId){
		this._closeDialog(null, dialogId);
	},

	_closeDialog: function(event, dialogId){
		var d = this.dialogs[dialogId];
		//d.fade(0).addEvent('onComplete', this._hiddenMask.bindWithEvent(this, Number(d.style.zIndex) - 1));
		d.fade(0);
		(function(){this._hiddenMask(null, Number(d.style.zIndex) - 1)}.bind(this)).delay(500);
	},

	_genDialog: function(dialog, options){
		var div = document.createElement('div');
		div.innerHTML = options.skeleton ? options.skeleton :
			K2.dialogSkeleton ? K2.dialogSkeleton :
			'<table border="0" cellpadding="1" cellspacing="2" bgcolor="#E9E6DE" style="border:1px solid #FFF">' +
			'<tr><td id="DLG_TITLE"></td></tr>' +
			'<tr><td id="DLG_BODY" bgcolor="#FFFFFF" style="border:1px solid #CCC;padding:5px"></td></tr></table>';
		var header = '<table width="100%" border="0" cellpadding="0" cellspacing="0" class="dlg-header"><tr>' +
			'<td width="99%" nowrap style="padding:4px 10px 4px 2px">' + 
			'<img src="' + (options.icon || K2.Icon.dialog) + '" align="absmiddle" style="margin-right:2px">' + options.title + '</td>' +
			'<td width="1%" style="padding-right:3px"><img src="' + K2.Icon.close + '" border="0" class="closeIcon" style="cursor:pointer"></td></tr></table>';
		$(div).getElement('[id=DLG_TITLE]').innerHTML = header;
		$(div).getElement('[id=DLG_TITLE]').setStyle('cursor', 'move');

		$(div).getElement('[id=DLG_BODY]').appendChild(dialog);
		document.body.appendChild(div);

		var closeIcon = $(div).getElement('img[class=closeIcon]');
		closeIcon.onclick = this._closeDialog.bindWithEvent(this, dialog.id);

		$(div).makeDraggable({handle: $(div).getElement('[id=DLG_TITLE]')});
		$(div).fade('hide');
		dialog.style.display = 'block';
		K2._center(div);

		return div;
	},

	// masks ...
	_adjustmentMasks: function(){
		var html = document.getElementsByTagName("html")[0];
		$$('div[id^=mask]').each(function(m){
			m.style.width = html.scrollWidth > html.clientWidth? html.scrollWidth + 'px' : html.clientWidth + 'px';
			m.style.height = html.scrollHeight > html.clientHeight? html.scrollHeight + 'px' : html.clientHeight + 'px';
		});
	},

	_getTopZIndex: function(){
		var z = 0;
		$$('div[id^=mask]').each(function(m){ if(m.style.zIndex > z) z = m.style.zIndex; });
		return z;
	},
	
	_getMaskCount: function(){
		return $$('div[id^=mask]').length;
	},

	_showMask: function(){
		var mask = this._genMask(this._getMaskCount() == 0 ? 101 : this._getTopZIndex() + 100);
		return mask.style.zIndex;
	},

	_hiddenMask: function(event, zIdx){
		$('mask' + zIdx).destroy();
	},

	_genMask: function(zIdx){
		var html = document.getElementsByTagName('html')[0];
		var mask = new Element('div', {
			'id': 'mask' + zIdx,
		    'styles': {
        		'display': 'block',
		        'position': 'absolute',
		        'background-color': '#808080',
		        'opacity': .5,
		        'filter': 'alpha(opacity=50)',
		        'cursor': 'not-allowed',
		        'top': 0,
		        'left': 0,
		        'width': html.scrollWidth > html.clientWidth? html.scrollWidth + 'px' : html.clientWidth + 'px',
		        'height': html.scrollHeight > html.clientHeight? html.scrollHeight + 'px' : html.clientHeight + 'px',
		        'zIndex': zIdx
    		}
    	});		
		document.body.appendChild(mask);
		return mask;
	}
});


// ** 03. FormValidator.js **********************************
K2.FormValidator = new Class({
	name: K2.name + '(K2.FormValidator)',

	initialize: function(formId, url, options){
		this.form = $(formId);
		this.url = url;
		if(!this.form){
			alert(this.name + ":\nCan't find form: " + formId);
			return;
		}
		this._setOptions(options);
		this.submit = this.form.getElement('input[name=submit]');
		if(!this.submit) this.submit = this.form.getElement('input[type=submit]');
		if(!this.submit){
			alert(this.name + ":\nCan't find submit button in form('" + formId + "')'");
			return;
		}
		this.properties = new Hash();
		this.propOptions = new Hash();
		this.tips = new Hash();
		this.validating = new Hash();
		this.validated = new Hash();
		this.remaining = false;
		this.form.onsubmit = this._validateRemaining.bindWithEvent(this, this.submit.onclick);
		this.submit.onclick = this.form.onsubmit;
		this.form.getElements('input[type=text]').extend(this.form.getElements('input[type=password]')).extend(this.form.getElements('textarea')).each(function(item){
			this._updatePropStyle(item, 'normal');
			item.addEvents({
				'focus': function(){
					if(this.validated.get(item.id) == 'success')	 this._updatePropStyle(item, 'focus');
					else if(this.validated.get(item.id) == 'info') this._updatePropStyle(item, 'info');
					else if(this.validated.get(item.id) == 'alert') this._updatePropStyle(item, 'alert');
					else if(this.validated.get(item.id) == 'error') this._updatePropStyle(item, 'error');
					else this._updatePropStyle(item, 'focus');
				}.bind(this),
				'blur': function(){ this._updatePropStyle(item, 'normal'); }.bind(this),
				'keyup': function(){
					if(this.validated.get(item.id) != ''){
						this.validated.set(item.id, '');
						this._updatePropStyle(item, 'focus');
					}
				}.bind(this),
				'mouseenter': function(){
					var tip = this.tips.get(item.id);
					if(tip && ['info', 'alert', 'error'].contains(this.validated.get(item.id))){
						tip.setStyle('display', 'block');
						var tipContent = tip.getElement('[id=content]');
						if(tip.getElement('[id=arrow]')) tip.getElement('[id=arrow]').destroy();
						if(item.getCoordinates($$('html')[0]).top - $$('html')[0].scrollTop >= tip.getSize().y + 20){
							var src = 'images/tip-' + this.validated.get(item.id) + '-arr-btm.gif'
							new Element('img', {'id': 'arrow', 'src': src, 'height': 20}).injectAfter(tip.getElement('[id=content]'));
							tipContent.setStyles({'background-position': 'top center', 'padding': '12px 0px 2px 0px'});
							tip.setStyles({'display': 'block', 'top': item.getTop() - tip.getSize().y + 9, 'left': item.getLeft() - 15});
						}
						else{
							var src = 'images/tip-' + this.validated.get(item.id) + '-arr-top.gif'
							new Element('img', {'id': 'arrow', 'src': src, 'height': 20}).injectBefore(tip.getElement('[id=content]'));
							tipContent.setStyles({'background-position': 'bottom center', 'padding': '2px 0px 12px 0px'});
							tip.setStyles({'display': 'block', 'top': item.getTop() + item.getSize().y - 9, 'left': item.getLeft() - 15});
						}
					}
				}.bind(this),
				'mouseleave': function(){
					var tip = this.tips.get(item.id);
					if(tip) tip.setStyles({'display': 'none'});
				}.bind(this)
			});
		}.bind(this));
	},

	_validateRemaining: function(event, fn){
		if(!this.remaining){
			this.remaining = true;
			var group = new K2.Group();
			var ps = this.properties.getKeys();
			var ajaxs = new Array();
			for(var i = 0; i < ps.length; i += 1){
				if(this.validated.get(ps[i]) != 'success' && this.validated.get(ps[i]) != 'info' /*&& !this.validating.get(ps[i])*/){
					var options = this.propOptions.get(ps[i])
					var ajax = this._getAjax(ps[i], options.encode, options.key);
					ajaxs = ajaxs.concat(ajax);
					group.add(ajax);
				}
			}

			if(group.size() > 0){
				group.addEvent('onComplete', function(){
					var ok = true;
					for(var i = 0; i < ps.length; i += 1){
						if(this.validated.get(ps[i]) != 'success' && this.validated.get(ps[i]) != 'info'){
							this.properties.get(ps[i]).select();
							this.properties.get(ps[i]).focus();
							ok = false;
							break;
						}
					}
					if(ok && fn) fn.attempt();
					this.remaining = false;
				}.bind(this));
				ajaxs.each(function(ajax){ajax.send();});
			}
			else{
				if(fn) fn.attempt();
				this.remaining = false;
			}
		}
		return false;
	},

	reset: function(){
		this.form.reset();
		this.validating = new Hash();
		this.validated = new Hash();
		this.remaining = false;
		this.properties.each(function(prop, key){this._updatePropStyle(prop, 'normal');}.bind(this));
	},

	_setOptions: function(options) {
		this.options = {
			successIco: K2.Icon.success16,
			infoIco: K2.Icon.info16,
			alertIco: K2.Icon.alert16,
			errorIco: K2.Icon.error16
		}
		Object.extend(this.options, options || {});
	},

	addProperty: function(propertyId, options){
		options = $merge({event: 'blur', encode: false, key: ''}, options);
		if(!this.form) return;
		var property = this.form.getElement('[id=' + propertyId + ']');
		if(!property){
			alert(this.name + ':\nCan\'t find property: "' + propertyId + '" in form(' + this.form.id + ')');
			return;
		}
		this.properties.set(propertyId, property);
		this.propOptions.set(propertyId, options);
		var tip = new Element('div', {
			'styles': {
				'position': 'absolute',
				'top': '0',
				'left': '0',
				'display': 'none',
				'z-index': '9999'
			}
		});
		var content = new Element('div', {'id': 'content'});
		content.grab(new Element('img', {'id': 'contentIco', 'align': 'left', 'styles': {'margin': '0px 5px 0px 12px'}}));
		content.grab(new Element('div', {'id': 'contentText', 'styles': {'padding': '0px 12px'}}));
		tip.grab(content);
		this.tips.set(propertyId, tip.inject(document.body));
		this.validating.set(propertyId, false);
		this.validated.set(propertyId, '');
		$(property).addEvent(options.event, this._validating.bindWithEvent(this, [propertyId, options.encode, options.key]));
	},

	_getAjax: function(propertyId, encode, key){
		var property = this.properties.get(propertyId);
		var data = encode ? (property.value.trim().length == 0 ? '' : new K2.MD5().encode(property.value + key) + '==') : property.value.trim();
		var ajax = new Request.JSON({
			url: this.url,
			method: 'get',
			data: {form: this.form.id, property: (property.id || property.name), data: data},
			onComplete: this._onComplete.bindWithEvent(this, propertyId)
		});
		return ajax;
	},

	_validating: function(event, propertyId, encode, key){
		if(this.remaining) return;
		if(this.validating.get(propertyId)) return;
		this.validating.set(propertyId, true);
		this._getAjax(propertyId, encode, key).send();
	},

	_onComplete: function(r, propertyId){
		//var r = Json.evaluate(transport);
		if(r.validate == '') return;
		this.setPropertyStatus(propertyId, r.validate, r.message);
	},
	
	setPropertyStatus: function(propId, status, tipMsg){
		var tip = this.tips.get(propId);
		tip.style.display = 'none';
		tip.getElement('[id=contentText]').set('text', tipMsg);
		this._updatePropStyle(this.properties.get(propId), status);
		this._updateTipStyle(tip, status);
		
		this.validating.set(propId, false);
		this.validated.set(propId, status);
	},
	
	_updatePropStyle: function(prop, status){
		prop.removeClass('validate-normal');
		prop.removeClass('validate-focus');
		prop.removeClass('validate-success');
		prop.removeClass('validate-info');
		prop.removeClass('validate-alert');
		prop.removeClass('validate-error');
		if(status == 'normal' || status == 'focus')
			prop.addClass('validate-' + status);
		else{
			prop.addClass('validate-normal');
			prop.addClass('validate-' + status);
		}
	},
	
	_updateTipStyle: function(tip, status){
		var tipContent = tip.getElement('[id=content]');
		var tipIco = tip.getElement('[id=contentIco]');
		tipContent.removeClass('validate-infoTip');
		tipContent.removeClass('validate-alertTip');
		tipContent.removeClass('validate-errorTip');
		tipContent.addClass('validate-' + status + 'Tip');
		tipIco.src = status == 'info' ? K2.Icon.indo16 : status == 'alert' ? K2.Icon.alert16 : K2.Icon.error16;
	}
});

// ** 04. MD5_Encoder.js **********************************
K2.MD5 = new Class({
	name: K2.name + '(K2.MD5)',

	initialize: function(){
		this.b64pad = "";  /* base-64 pad character. "=" for strict RFC compliance   */
		this.chrsz = 8;   /* bits per input character. 8 - ASCII; 16 - Unicode      */
	},

	encode: function(s){
		return this._binl2b64(this._core_md5(this._str2binl(s), s.length * this.chrsz));
	},

	_core_md5: function(x, len){
		function safe_add(x, y){
			var lsw = (x & 0xFFFF) + (y & 0xFFFF);
			var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
			return (msw << 16) | (lsw & 0xFFFF);
		}

		function rol(num, cnt){
			return (num << cnt) | (num >>> (32 - cnt));
		}

		function cmn(q, a, b, x, s, t){
			return safe_add(rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
		}

		function ff(a, b, c, d, x, s, t){
			return cmn((b & c) | ((~b) & d), a, b, x, s, t);
		}

		function gg(a, b, c, d, x, s, t){
			return cmn((b & d) | (c & (~d)), a, b, x, s, t);
		}

		function hh(a, b, c, d, x, s, t){
			return cmn(b ^ c ^ d, a, b, x, s, t);
		}

		function ii(a, b, c, d, x, s, t){
			return cmn(c ^ (b | (~d)), a, b, x, s, t);
		}

		x[len >> 5] |= 0x80 << ((len) % 32);
		x[(((len + 64) >>> 9) << 4) + 14] = len;
		var a =  1732584193;
		var b = -271733879;
		var c = -1732584194;
		var d =  271733878;

		for(i = 0; i < x.length; i += 16){
			var olda = a;
			var oldb = b;
			var oldc = c;
			var oldd = d;
			a = ff(a, b, c, d, x[i+ 0], 7 , -680876936);
			d = ff(d, a, b, c, x[i+ 1], 12, -389564586);
			c = ff(c, d, a, b, x[i+ 2], 17,  606105819);
			b = ff(b, c, d, a, x[i+ 3], 22, -1044525330);
			a = ff(a, b, c, d, x[i+ 4], 7 , -176418897);
			d = ff(d, a, b, c, x[i+ 5], 12,  1200080426);
			c = ff(c, d, a, b, x[i+ 6], 17, -1473231341);
			b = ff(b, c, d, a, x[i+ 7], 22, -45705983);
			a = ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
			d = ff(d, a, b, c, x[i+ 9], 12, -1958414417);
			c = ff(c, d, a, b, x[i+10], 17, -42063);
			b = ff(b, c, d, a, x[i+11], 22, -1990404162);
			a = ff(a, b, c, d, x[i+12], 7 ,  1804603682);
			d = ff(d, a, b, c, x[i+13], 12, -40341101);
			c = ff(c, d, a, b, x[i+14], 17, -1502002290);
			b = ff(b, c, d, a, x[i+15], 22,  1236535329);
			a = gg(a, b, c, d, x[i+ 1], 5 , -165796510);
			d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
			c = gg(c, d, a, b, x[i+11], 14,  643717713);
			b = gg(b, c, d, a, x[i+ 0], 20, -373897302);
			a = gg(a, b, c, d, x[i+ 5], 5 , -701558691);
			d = gg(d, a, b, c, x[i+10], 9 ,  38016083);
			c = gg(c, d, a, b, x[i+15], 14, -660478335);
			b = gg(b, c, d, a, x[i+ 4], 20, -405537848);
			a = gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
			d = gg(d, a, b, c, x[i+14], 9 , -1019803690);
			c = gg(c, d, a, b, x[i+ 3], 14, -187363961);
			b = gg(b, c, d, a, x[i+ 8], 20,  1163531501);
			a = gg(a, b, c, d, x[i+13], 5 , -1444681467);
			d = gg(d, a, b, c, x[i+ 2], 9 , -51403784);
			c = gg(c, d, a, b, x[i+ 7], 14,  1735328473);
			b = gg(b, c, d, a, x[i+12], 20, -1926607734);
			a = hh(a, b, c, d, x[i+ 5], 4 , -378558);
			d = hh(d, a, b, c, x[i+ 8], 11, -2022574463);
			c = hh(c, d, a, b, x[i+11], 16,  1839030562);
			b = hh(b, c, d, a, x[i+14], 23, -35309556);
			a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
			d = hh(d, a, b, c, x[i+ 4], 11,  1272893353);
			c = hh(c, d, a, b, x[i+ 7], 16, -155497632);
			b = hh(b, c, d, a, x[i+10], 23, -1094730640);
			a = hh(a, b, c, d, x[i+13], 4 ,  681279174);
			d = hh(d, a, b, c, x[i+ 0], 11, -358537222);
			c = hh(c, d, a, b, x[i+ 3], 16, -722521979);
			b = hh(b, c, d, a, x[i+ 6], 23,  76029189);
			a = hh(a, b, c, d, x[i+ 9], 4 , -640364487);
			d = hh(d, a, b, c, x[i+12], 11, -421815835);
			c = hh(c, d, a, b, x[i+15], 16,  530742520);
			b = hh(b, c, d, a, x[i+ 2], 23, -995338651);
			a = ii(a, b, c, d, x[i+ 0], 6 , -198630844);
			d = ii(d, a, b, c, x[i+ 7], 10,  1126891415);
			c = ii(c, d, a, b, x[i+14], 15, -1416354905);
			b = ii(b, c, d, a, x[i+ 5], 21, -57434055);
			a = ii(a, b, c, d, x[i+12], 6 ,  1700485571);
			d = ii(d, a, b, c, x[i+ 3], 10, -1894986606);
			c = ii(c, d, a, b, x[i+10], 15, -1051523);
			b = ii(b, c, d, a, x[i+ 1], 21, -2054922799);
			a = ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
			d = ii(d, a, b, c, x[i+15], 10, -30611744);
			c = ii(c, d, a, b, x[i+ 6], 15, -1560198380);
			b = ii(b, c, d, a, x[i+13], 21,  1309151649);
			a = ii(a, b, c, d, x[i+ 4], 6 , -145523070);
			d = ii(d, a, b, c, x[i+11], 10, -1120210379);
			c = ii(c, d, a, b, x[i+ 2], 15,  718787259);
			b = ii(b, c, d, a, x[i+ 9], 21, -343485551);
			a = safe_add(a, olda);
			b = safe_add(b, oldb);
			c = safe_add(c, oldc);
			d = safe_add(d, oldd);
		}
		return Array(a, b, c, d);
	},

	_str2binl: function(str){
		var bin = Array();
		var mask = (1 << this.chrsz) - 1;
		for(var i = 0; i < str.length * this.chrsz; i += this.chrsz)
			bin[i>>5] |= (str.charCodeAt(i / this.chrsz) & mask) << (i%32);
		return bin;
	},

	_binl2b64: function(binarray){
		var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
		var str = "";
		for(var i = 0; i < binarray.length * 4; i += 3){
			var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
				| (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
				| ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
			for(var j = 0; j < 4; j += 1){
				if(i * 8 + j * 6 > binarray.length * 32) str += this.b64pad;
				else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
			}
		}
		return str;
	}
});

// ** 05. InPlaceEditor.js **********************************
K2.IPE = new Class({
	name: K2.name + '(K2.IPE)',

	initialize: function(ele, url, id, property, options){
		this.element = $(ele);
		this.element.setStyle('padding', 2);
		this.element.set('tween', {duration: 300, link: 'cancel'});
		this.setValue(-1);

		this.url = url;
		this.id = id;
		this.property = property;
		this.options = $merge({
			localeId: -1,
			tip: '',
			highlightColor: '#FFC',
			multiLine: false,
			focusStyle: 'ipe-focus',
			onComplete: ''
		}, options);

		if(this.options.width) this.element.setStyle('width', this.options.width);
		if(this.options.multiLine && this.options.height){
			this.element.setStyle('overflow', 'auto');
			this.element.setStyle('height', this.options.height);
		}
		this.editing = false;
		this.saving = false;
		this.options.bg = K2._getBgColor(this.element);
		if(this.options.tip){
			this.element.setProperty('title', this.options.tip);
			this.tip = new Tips(this.element, {showDelay:1000});
		}
		this.element.addEvent('mouseover', this._mouseOver.bindWithEvent(this));
		this.element.addEvent('mouseout', this._mouseOut.bindWithEvent(this));
		this.element.addEvent('click', this._editing.bindWithEvent(this));
	},

	setOptions: function(options) {
		Object.extend(this.options, options || {});
	},

	setId: function(id){
		this.id = id;
	},

	setValue: function(value){
		var text = value == -1 ? this.element.get('html') : value + '';
		this.element.set('text', text);
		//text = text.replace(/\\r\\n/gi, '\r\n');
		//this.element.innerHTML = text.length == 0 ?
		//	'&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp' : text.replace(/\r\n/gi, '<br>');
	},

	_mouseOver: function(){
		if(this.saving || this.editing) return;
		this.element.tween('background-color', this.options.highlightColor);
	},

	_mouseOut: function(){
		if(this.saving || this.editing) return;
		this.element.tween('background-color', this.options.bg);
		(function(){this.element.style.background='transparent'}.bind(this)).delay(500);
	},

	_editing: function(){
		if(this.saving || this.editing) return;
		this.editing = true;
		this.element.setStyle('display', 'none');
		if(!this.form){
			this.form = this._createForm();
			this.form.injectAfter(this.element);
		}
		this.field.addEvent('blur', this._leftEditing.bindWithEvent(this));
		this.field.setAttribute('value', this.element.get('text'));
		this.form.setStyle('display', 'block');
		this.field.focus();
		if(!this.options.multiLine) this.field.select();

		if(this.options.tip)
			(function(){this.tip.hide()}.bind(this)).delay(1000);
	},

	_createForm: function(){
		var form = document.createElement("form");
		form = $(form);
		form.setStyle('margin', '0px');
		form.onsubmit = this._submit.bind(this);

		if(!this.options.multiLine){
			this.field = new Element('input', {
				'type': 'text',
				'value': this.element.get('text'),
				'class': this.options.focusStyle
			});
		}
		else{
			this.field = new Element('textarea', {
				'value': this.element.get('text'),
				'class': this.options.focusStyle
			});
		}
		if(this.options.width) this.field.setStyle('width', this.options.width);
		if(this.options.multiLine && this.options.height) this.field.setStyle('height', this.options.height);
		form.appendChild(this.field);
		return form;
	},

	_leftEditing: function(event){
		if(!this.saving && this.editing){
			this.editing = false;
			var editingValue = this.field.get('value');
			if(editingValue != this.element.get('text') && confirm('update?')) this._doSaving(editingValue);
			else this._doNotThing();
		}
	},

	_submit: function(event){
		//this.field.removeEvent('blur', this._leftEditing.bindWithEvent(this));
		this.saving = true;
		this.editing = false;
		var editingValue = this.field.get('value').trim();
		if(editingValue != this.element.get('text')) this._doSaving(editingValue);
		else this._doNotThing(editingValue);
		return false;
	},

	_doNotThing: function(){
		this.saving = false;
		this.editing = false;
		this.element.setStyle('display', '');
		if(this.form) this.form.setStyle('display', 'none');
		this._mouseOut();
	},

	_doSaving: function(value){
		this.saving = true;
		this.editing = false;
		this.form.setStyle('display', 'none');
		if(!this.savingDiv){
			this.savingDiv = this._createSavingDiv();
			this.savingDiv.injectAfter(this.element);
		}
		this.savingDiv.setStyle('display', '');
		var ajax = new Request.JSON({
			url: this.url,
			method: 'post',
			data: {id: this.id, localeId: this.options.localeId, property: this.property, original: this.element.get('text'), updated: value},
			onComplete: this._onComplete.bindWithEvent(this)
		});
		ajax.send();
	},

	_createSavingDiv: function(){
		var div = document.createElement("div");
		div = $(div);
		div.setAttribute('nowrap', 'nowrap');
		div.set('text', 'saving...');
		div.setStyles({'font-size': '7pt', 'color': '#AAA'});
		var img = new Element('img', {
			'src': K2.Icon.loading,
			'align': 'absmiddle',
			'hspace': '2'
		});
		img.injectTop(div);
		return div;
	},

	_onComplete: function(r){
		//var r = Json.evaluate(transport);
		this.savingDiv.setStyle('display', 'none');
		if(r.success){
			this.setValue(r.value);
			this.element.setStyle('display', '');
			this._mouseOut();
			this.editing = false;
			if($type(this.options.onComplete) == 'function')
				this.options.onComplete.attempt();
		}
		else{
			this.form.setStyle('display', '');
			this.field.select();
			alert(r.message);
			this.editing = true;
		}
		this.saving = false;
	}
});

// ** 06. InPlaceSelector.js **********************************
K2.IPS = new Class({
	Extends: K2.IPE,
	
	name: K2.name + '(K2.IPS)',

	initialize: function(ele, url, id, property, values, options){
		this.parent(ele, url, id, property, options);
		if($type(values) != 'array' || values.length < 1){
			alert(this.name + ':\nArgument: "values" must be an array. (length >= 1)');
			return;
		}
		this.setOptionValues(values);

		if(this.options.tip){
			this.element.setProperty('title', this.options.tipText);
			this.tip = new Tips(this.element, {showDelay:1000});
		}
	},

	setOptionValues: function(values){
		this.originalText = this.element.get('text').clean();
		if(this.id != 0 && !this._validateValues(values, this.originalText.toLowerCase())){
			alert(this.name + ':\nArgument: "values" can not seek init value(' + this.originalText + ')');
			return;
		}
		this.values = $type(values[0]) == 'array' ?  this._valueHash(values) : values;
	},

	_validateValues: function(values, text){
		var result = false;
		values.each(function(ele){
			if(result) return;
			if($type(ele) == 'array'){
				if(ele[0].toLowerCase() == text){
					this.originalValue = ele[1];
					result = true;
					return;
				}
			}
			else if((ele + '').toLowerCase() == text){
				result = true;
				return;
			}
		});
		return result;
	},

	_valueHash: function(values){
		var hash = new Hash();
		values.each(function(ele){
			hash.set(ele[1], ele[0]);
		});
		return hash;
	},

	_editing: function(){
		if(this.saving || this.editing) return;
		this.editing = true;
		this.element.setStyle('display', 'none');
		if(this.form) this.form.destroy();
		this.form = this._createForm();
		this.form.injectBefore(this.element);
		this.form.setStyle('display', '');
		this.field.focus();

		if(this.options.tip)
			(function(){this.tip.hide()}.bind(this)).delay(1000);
	},

	_createForm: function(){
		var form = document.createElement("form");
		form = $(form);
		form.setStyle('margin', '0px');
		form.onsubmit = this._submit.bind(this);

		this.field = new Element('select', {
			'class': 'ipe-normal',
			'events': {
				'change': this._leftEditing.bindWithEvent(this),
				'blur': this._leftEditing.bindWithEvent(this)
			}
		});
		if(this.options.width) this.field.setStyle('width', this.options.width);

		if($type(this.values) == 'array'){
			this.values.each(function(element){
				element += '';
				var opt = new Element('option', {'value': element});
				opt.set('text', element);
				if(this.element.get('text').toLowerCase().clean() == element.toLowerCase().clean())
					opt.setAttribute('selected', 'selected');
				this.field.appendChild(opt);
			}.bind(this));
		}
		else{
			this.values.each(function(element, key){
				element += '';
				var opt = new Element('option', {'value': key});
				opt.set('text', element);
				if(this.element.get('text').toLowerCase().clean() == element.toLowerCase().clean()){
					opt.setAttribute('selected', 'selected');
					this.originalValue = key;
				}
				this.field.appendChild(opt);
			}.bind(this));
		}
		form.appendChild(this.field);
		return form;
	},

	_leftEditing: function(event){
		if(!this.saving && this.editing){
			this.editing = false;
			var editingValue = this.field.get('value').trim();
			if(editingValue != this.originalValue) this._doSaving(editingValue);
			else this._doNotThing();
		}
	},

	_doSaving: function(value){
		this.saving = true;
		this.editing = false;
		this.form.setStyle('display', 'none');
		if(!this.savingDiv){
			this.savingDiv = this._createSavingDiv();
			this.savingDiv.injectAfter(this.element);
		}
		this.savingDiv.setStyle('display', '');
		var ajax = new Request.JSON({
			url: this.url,
			method: 'post',
			data: {id: this.id, localeId: this.options.localeId, property: this.property, original: this.originalValue, updated: value},
			onComplete: this._onComplete.bindWithEvent(this)
		});
		ajax.send();
	},

	_onComplete: function(r){
		//var r = Json.evaluate(transport);
		this.savingDiv.setStyle('display', 'none');
		if(r.success){
			this.originalValue = r.value;
			this.originalText = ($type(this.values) == 'array') ? r.value : this.values.get(r.value);
			this.element.innerHTML = this.originalText == '' ?
				'&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp':
				this.originalText.replace(/\n/gi, '<br>');
			this.element.setStyle('display', '');
			this._mouseOut();
			this.editing = false;
			if($type(this.options.onComplete) == 'function')
				this.options.onComplete.attempt();
		}
		else{
			this.form.setStyle('display', '');
			alert(r.message);
			this.editing = true;
		}
		this.saving = false;
	}
});

// ** 07. Tabs.js **********************************
K2.Tabs = new Class({
	name: K2.name + '(K2.Tabs)',

	initialize: function(options){
		this.contents = new Hash();
		this.selected = '';
		this.options = $merge({
			tabClass: 'tab-unselected',
			tabOverClass: 'tab-over',
			tabSelectedClass: 'tab-selected',
			contentClass: 'tab-content'
		}, options);
	},

	addTab: function(tabId, contentId, onClick){
		var tab = $(tabId);
		if(!tab){
			alert(this.name + ':\nCan\'t find tab: "' + tabId + '"');
			return;
		}

		var ids = new Array();
		if($type(contentId) != 'array') ids[0] = contentId;
		else ids = contentId;
		
		var content = new Array();
		ids.each(function(ele){
			if(!$(ele)){
				alert(this.name + ':\nCan\'t find tab content: "' + ele + '"');
				return;
			}
			else{
				content.push($(ele));
				$(ele).addClass(this.options.contentClass);
			}
		}, this);

		tab.setStyle('cursor', 'pointer');
		this.contents.set(tabId, content);
		if(this.contents.getKeys().length == 1){
			this._tabSelected(null, tab, onClick);
		}
		else{
			content.each(function(ele){ele.style.display = 'none';});
			tab.addClass(this.options.tabClass);
		}
		tab.addEvent('click', this._tabSelected.bindWithEvent(this, [tab, onClick]));
		tab.addEvent('mouseover', this._tabMouseOver.bindWithEvent(this, tab));
		tab.addEvent('mouseout', this._tabMouseOut.bindWithEvent(this, tab));
	},

	_tabSelected: function(event, tab, fn){
		this.contents.getKeys().each(function(tabId){
			this._unselected($(tabId));
		}, this);
		tab.removeClass(this.options.tabClass);
		tab.removeClass(this.options.tabOverClass);
		tab.addClass(this.options.tabSelectedClass);
		this.contents.getValues().each(function(content){
			content.each(function(ele){ele.style.display = 'none';});
		}, this);
		this.contents.get(tab.id).each(function(ele){
			ele.style.display = 'block';
		});
		this.selected = tab.id;
		if(fn && $type(fn) == 'function')
			fn.attempt();
	},

	_unselected: function(tab){
		tab.removeClass(this.options.tabSelectedClass);
		tab.removeClass(this.options.tabOverClass);
		tab.addClass(this.options.tabClass);
	},

	_tabMouseOver: function(event, tab){
		if(tab.id != this.selected){
			tab.addClass(this.options.tabOverClass);
		}
	},

	_tabMouseOut: function(event, tab){
		if(tab.id != this.selected){
			tab.removeClass(this.options.tabOverClass);
		}
	}
});


// ** 08. Pager.js **********************************
K2.Pager = new Class({
	name: K2.name + '(K2.Pager)',

	initialize: function(data, indicator, options){
		this.options = $merge({
			count: 20
		}, options);

		this.pageIndicator = $(indicator);
		if(this.pageIndicator == null){
			alert(this.name + ':\nCan not find "Page Indicator"(select object)!');
			return;
		}
		this.setData(data);
		this.countPerPage = isNaN(this.options.count) || this.options.count < 1 ? 20 : Math.round(this.options.count);
		this.setCurrPage(1);
	},
	
	getData: function(){
		return this.data;
	},
	
	setData: function(data){
		if($type(data) != 'array'){
			alert(this.name + ':\nData is not array type!');
			return;
		}
		this.data = data;
		this._genPageIndicator();
	},
	
	updateCountPerPage: function(count){
		this.countPerPage = isNaN(count) || count < 1 ? 20 : Math.round(count);
		this.setCurrPage(1);
		this._genPageIndicator();
	},
	
	setCurrPage: function(idx){
		this.currPage = idx < 1 ? 1 : idx > this.getMaxPage() ? this.getMaxPage() : idx;
		this.pageIndicator.selectedIndex = this.currPage - 1;
	},
	
	firstPage: function(){
		this.setCurrPage(1);
	},
	
	previousPage: function(){
		this.setCurrPage(this.currPage - 1);
	},
	
	nextPage: function(){
		this.setCurrPage(this.currPage + 1);
	},
	
	lastPage: function(){
		this.setCurrPage(this.getMaxPage());
	},

	getMaxPage: function(){
		return this.data.length == 0 ? 1 : Math.ceil(this.data.length / this.countPerPage);
	},
	
	getCurrData: function(){
		var start = (this.currPage - 1) * this.countPerPage;
		var length = this.currPage < this.getMaxPage() ? this.countPerPage : this.data.length - start;
		var data = new Array();
		for(var i = 0; i < length; i++)
			data[i] = this.data[start + i]; 
		return data;
		//return this.data.copy(start, length);
	},
	
	getStartSeq: function(){
		return (this.currPage - 1) * this.countPerPage;
	},
	
	_genPageIndicator: function(){
		this.pageIndicator.empty();
		for(var i = 1, n = this.getMaxPage(); i <= n; i++){
			var opt = new Element('option', {'value': i});
			opt.appendText(i);
			if(i == this.currPage) opt.setAttribute('selected', 'selected');
			opt.injectInside(this.pageIndicator);
		}
	},
	
	genCountIndicator: function(target, counts){
		target = $(target);
		target.empty();
		if(counts == null) counts = [5, 10, 20, 50, 100];
		for(var i = 0, n = counts.length; i < n; i++){
			var opt = new Element('option', {'value': counts[i]});
			opt.appendText(counts[i]);
			if(counts[i] == this.countPerPage) opt.setAttribute('selected', 'selected');
			opt.injectInside(target);
		}
	}
});
// *************************************************

