LBP.ActionsBar = new Class({
	Implements: [Events, Options],
	options: {
		selector: null, // ten cua select muon dung lam actions dropdown
		table: null, // ten cua table chua cac checkbox
		buttonClassName: 'action-button', // class cua cac button ma chung ta se quan ly
		confirmClassName: 'must-confirm', // them confirm vao nhung button co class nay
		submitClassName: 'action-submit', // ten class cua button se dung de submit form
		defaultMessage: 'Bạn có thực sự muốn thực hiện hành động này', // message confirm
		emptyMessage: 'Bạn chưa đánh dấu đối tượng nào', // message khi empty check
		emptyFormMessage: 'Bạn chưa đánh dấu đối tượng nào',
		selectAllValue: 'all',
		formID: null,

		/**
		*	lam gi khi mot button duoc click
		*/
		onButtonClick: function(currentButton){
			this.defaultClick(currentButton);
		},

		/**
		*	lam gi khi submit duoc click
		*/
		onSubmit: function(currentButton){
			this.defaultSubmit(currentButton);
		}
	},

	defaultClick: function(currentButton){
		if (currentButton.hasClass(this.options.submitClassName)){
			this.fireEvent('onSubmit', [currentButton]);
		}
	},

	addMoreData: function(fieldName, fieldValue){
		if (this.options.formID != null){
			this.form = $(this.options.formID);
		}
		else {
			this.form = actionsBar.getParent('form');
		}

		if (this.form == null){
			return;
		}

		if (!this.form.getElement('input[name='+ fieldName +']')){
			new Element(
				"input", {
				'type': 'hidden',
				'name': fieldName,
				'value': fieldValue
			}).injectInside(this.form);
		}
		else {
			this.form.getElement('input[name='+ fieldName +']').value = fieldValue;
		}
	},

	defaultSubmit: function(currentButton){
		if (this.options.formID != null){
			this.form = $(this.options.formID);
		}
		else {
			this.form = this.actionsBar.getParent('form');
		}

		if (this.form == null){
			alert(this.options.emptyFormMessage);
			return;
		}

		/**
		*	mac dinh la pass qua moi check
		*/
		var passed = true;

		/**
		*	kiem tra xem co item nao trong table da duoc danh dau hay chua
		*/
		if (this.options.table){
			var table = $(this.options.table);
			passed = this.checkSelected(table, this.options.emptyMessage);
		}

		if (!passed){
			return;
		}

        /**
		*	kiem tra xem button nay co can confirm ko
		*/
		if (currentButton.hasClass(this.options.confirmClassName)){
			passed = this.confirmAction(this.options.defaultMessage);
		}

		if (!passed){
			return;
		}

		/**
		*	kiem tra xem selector co can confirm hay ko
		*/
		if (this.options.selector){
			var selector = $(this.options.selector);
			passed = this.checkSelector(selector, this.options.defaultMessage);
		}

		if (!passed){
			return;
		}

		/**
		*	tat ca deu pass, submit form bang request handler, RH se tu kiem tra ajax
		*/
		LBP.submitForm(this.form, currentButton.id);
	},

	initialize: function(actionsBar, options){
		var actionsBar = $(actionsBar);
		if (!actionsBar){
			return;
		}
		if (actionsBar.retrieve('installed')){
			return;
		}
		actionsBar.store('installed', true);

		this.setOptions(options);
		this.actionsBar = actionsBar;
		this.buildActionsBar(actionsBar);
	},

	confirmAction: function(message){
		if (confirm(message)){
			return true;
		}
		else {
			return false;
		}
	},

	checkSelected: function(table, message){
		var count = 0;
		table.getElements('input[type=checkbox]').each(function(checkbox){
			if (checkbox.checked && checkbox.value != this.options.selectAllValue)
				count++;
		}.bind(this));
		if (count)
			return true;
		else
			alert(message);
			return false;
	},

	checkSelector: function(selector, message){
		if (selector.getElements('option')[ selector.selectedIndex ].hasClass(this.options.confirmClassName)){
			if (confirm(message)){
				return true;
			}
			else {
				return false;
			}
		}
		return true;
	},

	buildActionsBar: function(actionsBar){
		var buttons = actionsBar.getElements('.' + this.options.buttonClassName);
		$each(buttons, function(currentButton){
			currentButton.addEvent('click', function(){
				this.fireEvent('onButtonClick', [currentButton]);
			}.bind(this));
		}.bind(this));
	}
});

LBP.Alertx = new Class({
	Implements: [Options, Events, Chain],
	options: {
		duration: 8000,
		position: 'upperLeft',
		container: null,
		bodyFx: null,
		itemFx: null,
		margin: {x: 10, y: 10},
		offset: 10,
		cssName: 'alert',
		onShow: $empty,
		onHide: $empty,
		onRender: $empty,

        type: 'errorbx',
        txtTitle: '',
        opacity: '1'
	},

	initialize: function(options) {
		this.setOptions(options);
		this.items = [];
		this.container = $(this.options.container) || document;
	},

    generateAlert: function(element_html, options) {
        options = options || {};
        var alertCnt2 = new Element('div', { 'class': this.options.cssName+'-cnt-inner' } );
        alertCnt2.set('html', element_html);
        var alertCnt = new Element('div', { 'class': this.options.cssName+'-cnt' } ).adopt(alertCnt2);
        this.setOptions(options);
        if(!options.txtTitle){
            var items = alertCnt ;
        } else {
            var items = [ new Element('div', { 'class': this.options.cssName+'-title' }).appendText(options.txtTitle) ];
            items.push(alertCnt);
        }
        return this.inject(items, options);
    },

	inject: function(elements, options) {
		if (!this.body) this.render();
		options = options || {};

		var offset = [-this.options.offset, 0];
		var last = this.items.getLast();
		if (last) {
			offset[0] = last.retrieve(this.options.cssName+':offset');
			offset[1] = offset[0] + last.offsetHeight + this.options.offset;
		}
		var to = {'opacity': this.options.opacity };
		to[this.align.y] = offset;
        var itemBg = new Element("div", {
                'class': this.options.cssName+'-bg'
            });
		var item = new Element('div', {
			'class': this.options.cssName,
			'opacity': 0
		}).adopt(
			itemBg,
			elements
		);
        item.addClass(this.options.type);
		item.setStyle(this.align.x, 0).store(this.options.cssName+':offset', offset[1]).set('morph', $merge({
			unit: 'px',
			link: 'cancel',
			onStart: Chain.prototype.clearChain,
			transition: Fx.Transitions.Back.easeOut
		}, this.options.itemFx));

		var remove = this.remove.create({
			bind: this,
			arguments: [item],
			delay: 10
		});
		this.items.push(item.addEvent('click', remove));

		if (this.options.duration) {
			var over = false;
			var trigger = (function() {
				trigger = null;
				if (!over) remove();
			}).delay(this.options.duration);
			item.addEvents({
				mouseover: function() {
					over = true;
				},
				mouseout: function() {
					over = false;
					if (!trigger) remove();
				}
			});
		}
		item.inject(this.body).morph(to);
        itemBg.setStyle('height', item.offsetHeight+'px');
        this.fireEvent('onShow', [item, this.items.length]);
        return item;
		//return this.fireEvent('onShow', [item, this.items.length]);
	},

	remove: function(item) {
		var index = this.items.indexOf(item);
		if (index == -1) return this;
		this.items.splice(index, 1);
		item.removeEvents();
		var to = {opacity: 0};
		//to[this.align.y] = item.getStyle(this.align.y).toInt() - item.offsetHeight - this.options.offset;

		item.morph(to).get('morph').chain(item.destroy.bind(item));
		return this.fireEvent('onHide', [item, this.items.length]).callChain(item);
	},

	empty: function() {
		while (this.items.length) this.remove(this.items[0]);
		return this;
	},

	render: function() {
		this.position = this.options.position;
		if ($type(this.position) == 'string') {
			var position = {x: 'center', y: 'center'};
			this.align = {x: 'left', y: 'top'};
			if ((/left|west/i).test(this.position)) position.x = 'left';
			else if ((/right|east/i).test(this.position)) this.align.x = position.x = 'right';
			if ((/upper|top|north/i).test(this.position)) position.y = 'top';
			else if ((/bottom|lower|south/i).test(this.position)) this.align.y = position.y = 'bottom';
			this.position = position;
		}
		this.body = new Element('div', {'class': this.options.cssName+'-body'}).inject(document.body);
		//if (Browser.Engine.trident4) this.body.addClass(this.options.cssName+'-body-ugly');
		this.moveTo = this.body.setStyles.bind(this.body);
		this.reposition();
		if (this.options.bodyFx) {
			var morph = new Fx.Morph(this.body, $merge({
				unit: 'px',
				chain: 'cancel',
				transition: Fx.Transitions.Circ.easeOut
			}, this.options.bodyFx));
			this.moveTo = morph.start.bind(morph);
		}
		var repos = this.reposition.bind(this);
		window.addEvents({
			scroll: repos,
			resize: repos
		});
		this.fireEvent('onRender', this.body);
	},

	reposition: function() {
		var max = document.getCoordinates(), scroll = document.getScroll(), margin = this.options.margin;
		max.left += scroll.x;
		max.right += scroll.x;
		max.top += scroll.y;
		max.bottom += scroll.y;
		var rel = ($type(this.container) == 'element') ? this.container.getCoordinates() : max;
		this.moveTo({
			left: (this.position.x == 'right')
				? (Math.min(rel.right, max.right) - margin.x)
				: (Math.max(rel.left, max.left) + margin.x),
			top: (this.position.y == 'bottom')
				? (Math.min(rel.bottom, max.bottom) - margin.y)
				: (Math.max(rel.top, max.top) + margin.y)
		});
	}

});

LBP.AutocompleterLocal = new Class({

	Extends: Autocompleter.Local,

	initialize: function(el, options) {
		this.parent(el, [], options);
	},

	autoFeed: function(text) {
		this.tokens.include(text);
		return this;
	}
});

LBP.AutocompleterAjaxJson = new Class({

	Extends: Autocompleter.Ajax.Json

});

LBP.Calendar = new Class({
	'slideDuration': 500,
	'fadeDuration': 500,
	'transition': Fx.Transitions.Quart.easeOut,
	'startMonday': false,
	'filePath': 'inc/',
	'defaultView': 'month',
	'style': '',

	initialize: function(_container, _options) {
		//Add the provided options to this object by extending
		if(_options) $extend(this, _options);
		this.loading = false;
		this.container = _container = $(_container);
		var _class = this;

		//Insert the base into the container and initialize elements
		var  pars = 'default_view=' + this.defaultView;
		if(this.picker) {
			if($type(this.prefillDate) == 'object' && this.getInputDate(this.prefillDate)) pars += '&pickedDate='+ this.getInputDate(this.prefillDate);
			if(this.linkWithInput) pars += '&gotoPickedDate=1';
		}
		this.u('base', pars, function() {
			_class.mainLoader = _container.getElement('div[class=loaderA]');
			_class.tempLoader = _container.getElement('div[class=loaderB]');
			_class.label 	  = _container.getElement('span[class=label]');
			_class.arrowLeft  = _container.getElement('div[class=arrowLeft]');
			_class.arrowRight = _container.getElement('div[class=arrowRight]');
			_class.initializeCalendarFunctions();

			//Prefill/load picker date elements
			if(_class.picker) {
				/*if($type(_class.prefillDate) == 'object' && _class.getInputDate(_class.prefillDate)) _class.pick(_class.prefillDate);
				else if(_class.prefillDate == true) _class.pick(JSON.decode(_class.label.getProperty('date')));*/
			}
		}, _container);
	},

	initializeCalendarFunctions: function() {
		this.resetArrows();
		//Retrieve data (label, timestamp etc) which are stored as a Json string in the table attribute summary
		var vars = JSON.decode(this.mainLoader.getElement('table').getProperty('summary'));
		var _class = this;

		//Change the label
		this.label.removeClass('noHover').set('html', vars.label)
			.onclick = vars.parent ? function() { _class.u(vars.parent, 'ts=' + vars.ts + '&parent=' + vars.current, function() { _class.fade() }) } : null;

		//Hide arrows if necessary and add arrow click events
		if(vars.hide_left_arrow) this.hideLeftArrow();
		else if(vars.hide_right_arrow) this.hideRightArrow();

		this.arrowLeft.onclick  = function() { _class.u(vars.current, 'ts=' + vars.pr_ts, function() { _class.slideLeft() }) }
		this.arrowRight.onclick = function() { _class.u(vars.current, 'ts=' + vars.nx_ts, function() { _class.slideRight() }) }

		//Add cell click events
		var clickables = this.mainLoader.getElements('td');
		switch(vars.current) {
			case 'month':
				if(this.picker) {
					clickables.each(function(_clickable) {
						_clickable.onclick = function() {
							_class.pick(JSON.decode(_clickable.getProperty('date')));
							_class.mainLoader.getElements('td').each(function(_clickable) { _clickable.removeClass('selected') });
							this.addClass('selected');
						}
					});
				}
				break;
			case 'year':
				clickables.each(function(_clickable) {
					_clickable.onclick = function() { _class.u('month', 'ts=' + _clickable.getProperty('ts'), function() { _class.fade() }) }
				});
				break;
			case 'decade':
				this.label.addClass('noHover');
				clickables.each(function(_clickable) {
					_clickable.onclick = function() { _class.u('year', 'ts=' + _clickable.getProperty('ts') + '&m_ts=' + _clickable.getProperty('m_ts'), function() { _class.fade() }) }
				});
				break;
		}
	},

	//Ajax updater function which handles all requests
	u: function(_url, _pars, _onComplete, _id) {
		if(!this.loading && !this.transitioning) {
			var _class = this;
			this.loading = true;
			var element = $(_id ? _id : this.tempLoader);
			_pars += '&picker=' + (this.picker ? 1 : 0) + '&startMonday=' + (this.startMonday ? 1 : 0) + '&style=' +  this.style + '&view=' + _url;
			if(this.picker && this.getInputDate()) _pars += '&pickedDate='+ this.getInputDate();
			var myRequest = new Request(
				{
					method: 'post',
					url: this.filePath,
                    async: false,
					autoCancel: true,
					onComplete: function(data) {
						element.set('html', data);
						_onComplete();
						_class.loading = false;
					}
				}
			);
			//myRequest.setHeader('Cookie', 'aa');
			myRequest.send(_pars)
		}
	},

	slideLeft: function() {
		var _class = this;
		this.transitioning = true;
		this.tempLoader.setStyle('opacity', 1).set('tween', { duration: this.slideDuration, transition: this.transition }).tween('margin-left', [-164, 0]);
		this.mainLoader.setStyle('opacity', 1).set('tween', { duration: this.slideDuration, transition: this.transition, onComplete: function() { _class.transitioning = false } })
			.tween('margin-left', [0, 164]);
		this.switchLoaders();
	},

	slideRight: function() {
		var _class = this;
		this.transitioning = true;
		this.mainLoader.setStyle('opacity', 1).set('tween', { duration: this.slideDuration, transition: this.transition }).tween('margin-left', [0, -164]);
		this.tempLoader.setStyle('opacity', 1).set('tween', { duration: this.slideDuration, transition: this.transition, onComplete: function() { _class.transitioning = false } })
			.tween('margin-left', [164, 0]);
		this.switchLoaders();
	},

	fade: function(overRuleTrans) {
		var _class = this;
		this.transitioning = overRuleTrans ? false : true;
		this.tempLoader.setStyles({'opacity': 0, 'margin-left': 0});
		this.mainLoader.set('tween', { duration: this.fadeDuration, transition: this.transition}).fade('out');
		this.tempLoader.set('tween', { duration: this.fadeDuration, transition: this.transition,
			onComplete: function() {
					_class.tempLoader.setStyles({'opacity': 1, 'margin-left': -999});
					_class.transitioning = false;
				}
			}).fade('in');
		this.switchLoaders();
	},

	switchLoaders: function() {
		this.mainLoader = this.mainLoader.className == 'loaderA' ? this.container.getElement('div[class=loaderB]') : this.container.getElement('div[class=loaderA]');
		this.tempLoader = this.tempLoader.className == 'loaderA' ? this.container.getElement('div[class=loaderB]') : this.container.getElement('div[class=loaderA]');
		this.initializeCalendarFunctions();
	},

	resetArrows: function() {
		this.arrowLeft.setStyle('visibility', 'visible');
		this.arrowRight.setStyle('visibility', 'visible');
	},

	hideLeftArrow: function() {
		this.arrowLeft.setStyle('visibility', 'hidden');
	},

	hideRightArrow: function() {
		this.arrowRight.setStyle('visibility', 'hidden');
	}
});

LBP.Checkboxes = new Class({

	Implements: [Options, Events],
	options: {
		wrapperName: 'checkbox', // class
		divClick: null,
		checkboxClick: null,
		divOver: null,
		divOut: null
	},

	initialize: function(elements, options){
		this.setOptions(options);
        elements.each(function(theDiv){
			if (!theDiv.retrieve('checkboxInstalled')){
				theDiv.store('checkboxInstalled', true);

				theDiv.addEvent('click', function(){
					this.onDivClick(theDiv);
				}.bind(this));

				theDiv.addEvent('mouseover', function(){
					this.onDivOver(theDiv);
				}.bind(this));

                theDiv.addEvent('mouseout', function(){
					this.onDivOut(theDiv);
				}.bind(this));

				var checkbox = theDiv.getElement('input[type=checkbox]');

				checkbox.addEvent('click', function(){
					this.onCheckboxClick(checkbox, theDiv);
				}.bind(this));

				if (checkbox.checked)
					theDiv.addClass(this.options.wrapperName + '-checked');
			}
		}.bind(this));
	},

	onDivOver: function(theDiv){
        if (theDiv.hasClass(this.options.wrapperName + '-checked')){
			theDiv.addClass(this.options.wrapperName + '-checked-hover');
		}
		else {
			theDiv.addClass(this.options.wrapperName + '-hover');
		}
		(this.options.divOver || this.divOver).call(this, theDiv);
	},

	onDivOut: function(theDiv){
        theDiv.removeClass(this.options.wrapperName + '-checked-hover');
		theDiv.removeClass(this.options.wrapperName + '-hover');
		(this.options.divOut || this.divOut).call(this, theDiv);
	},

    onDivClick: function(theDiv){
		var checkbox = theDiv.getElement('input[type=checkbox]');
		checkbox.checked = !checkbox.checked;
		checkbox.fireEvent('click');
		(this.options.divClick || this.divClick).call(this, theDiv);
	},

	onCheckboxClick: function(checkbox, theDiv){
		if (checkbox.checked)
			theDiv.addClass(this.options.wrapperName + '-checked');
		else
			theDiv.removeClass(this.options.wrapperName + '-checked');
		(this.options.checkboxClick || this.checkboxClick).call(this, checkbox, theDiv);
	},

    divClick: function(theDiv){},
	checkboxClick: function(checkbox, theDiv){},
	divOver: function(theDiv){},
	divOut: function(theDiv){}
});

LBP.ColorPicker = new Class({
    Extends: MooRainbow,
	options:{
		imgPath: 'http://static.acc.vn/images/controls/editor/color_picker/',
		selectText: 'Chọn'
	}
});

LBP.Confirm = new Class({
    Implements: [Events, Options],
    options: {
    	message: 'Bạn có chắc chắn muốn thực hiện hành động này?'
	},
	initialize: function(links, options){
		links.each(function(link){
			link.removeEvents('click');
			link.addEvent('click', function(event){
				event.preventDefault();
                if (confirm(this.options.message)){
					if (LBP.Links.isAttached(link)){
						LBP.Links.forceSubmitLink(link);
					}
					else {
						window.location.href = link.get('href');
					}
				}
				else {
					return false;
				}

			}.bind(this));
		}.bind(this));
	}
});

LBP.DatePicker = new Class({
	Extends: LBP.Calendar,
    options: {
	'separateInput': false,
	'prefillDate': true,
	'linkWithInput': true,
	'leadingZero': true,
	'twoDigitYear': false,
	'separator': '/',
	'format': 'd/m/y',
	'openWith': null,
	'alignX': 'right',
	'alignY': 'inputTop',
	'offset': { 'x': 0, 'y': 0 },
	'style': '',
	'ieTransitionColor' : '#ffffff',
	'toggleDuration': 350
    },

	initialize: function(_element, _options) {
		//Add the provided options to this object by extending
		if(_options) $extend(this, _options);

		this.element = $(_element);
		if(!this.element) throw 'No (existing) element to create a datepicker for specified: new vlaDatePicker(ELEMENT, [options])';

		//Check if the user wants multiple input
		if(this.separateInput) {
			this.element.day   = this.element.getElement('input[name='+ this.separateInput.day +']');
			this.element.month = this.element.getElement('input[name='+ this.separateInput.month +']');
			this.element.year  = this.element.getElement('input[name='+ this.separateInput.year +']');
		}

		//Create the picker and calendar and inject in in the body
		this.picker = new Element('div', { 'class': 'lbp-calendar-picker' + (this.style != '' ? ' ' + this.style : '') }).inject($(document.body));
		this.pickerContent = new Element('div', { 'class': 'pickerBackground' }).inject(this.picker, 'top');
		this.parent(this.pickerContent);

		//Add events for showing and hiding the picker
		var _class = this;
		(this.openWith ? $(this.openWith) : this.element)
			.addEvent('focus',  function() { _class.show(); })
			.addEvent('click',  function() { _class.openWith ? _class.toggle() : _class.show() })
			.addEvent('change', function() { _class.hide(); });

		//If the datepicker is visible an outside click makes it hide
		document.addEvent('mousedown', function(e) { if(_class.outsideHide && _class.outsideClick(e, _class.picker)) _class.hide() });

		//linkWithInput
		if(this.linkWithInput) {
			if(this.separateInput) {
				this.element.day.addEvent('keyup',  function() { _class.linkedUpdate() });
				this.element.month.addEvent('keyup',  function() { _class.linkedUpdate() });
				this.element.year.addEvent('keyup',  function() { _class.linkedUpdate() });
			} else {
				this.element.addEvent('keyup',  function() { _class.linkedUpdate() });
			}
		}

		this.visible = false;
		this.outsideHide = false;
	},

	//Position the picker
	position: function() {
		var top, left;

		switch(this.alignX) {
			case 'left':
				left = this.element.getLeft();
				break;
			case 'center':
				var pickerMiddle = this.pickerContent.getStyle('width').toInt() / 2;
				if(pickerMiddle == 0) pickerMiddle = 83;
				left = this.element.getLeft() + (this.element.getSize().x / 2) - pickerMiddle -
						((parseInt(this.pickerContent.getStyle('padding-left')) + parseInt(this.pickerContent.getStyle('padding-right'))) / 2);
				break;
			case 'right': default:
				left = this.element.getLeft() + this.element.getSize().x;
				break;
		}

		switch(this.alignY) {
			case 'bottom':
				top = this.getPos(this.element).y + this.element.getSize().y;
				break;
			case 'top':
				top = this.getPos(this.element).y - parseInt(this.pickerContent.getStyle('height')) -
					(parseInt(this.pickerContent.getStyle('padding-top')) + parseInt(this.pickerContent.getStyle('padding-bottom')));
				break;
			case 'inputTop': default:
				top = this.getPos(this.element).y;
		}

		if(this.isNumber(this.offset.x)) left += this.offset.x;
		if(this.isNumber(this.offset.y)) top += this.offset.y;

		this.picker.setStyles({ 'top': top, 'left': left });
	},

	show: function() {
		this.position();
		if(!this.visible) {
			this.visible = true;
			var _class = this;
			this.picker.setStyles({ 'opacity': 0, 'display': 'inline' });
			if(Browser.Engine.trident5) this.picker.setStyle('background-color', this.ieTransitionColor); //Ugly transition fix for IE7
			this.picker.set('tween', { onComplete: function() {
					if(Browser.Engine.trident5) _class.picker.setStyle('background-color', 'transparent');
					_class.outsideHide = true;
				}, duration: this.toggleDuration }).fade('in');
		}
	},

	hide: function() {
		if(this.visible) {
			this.visible = false;
			var _class = this;
			if(Browser.Engine.trident5) this.picker.setStyle('background-color', this.ieTransitionColor); //Ugly transition fix for IE7
			this.picker.set('tween', { onComplete: function() { _class.picker.setStyle('display', 'none'); _class.outsideHide = false; }, duration: this.toggleDuration }).fade('out');
		}
	},

	toggle: function() {
		if(this.visible) this.hide();
		else this.show();
	},

	pick: function(_date) {
		if(this.leadingZero) {
			if(_date.day < 10)   _date.day = '0' + _date.day;
			if(_date.month < 10) _date.month = '0' + _date.month;
		}
		if(this.twoDigitYear) _date.year = _date.year.toString().substring(2, 4);

		if(this.separateInput) {
			if(this.element.day)   this.element.day.set('value', _date.day);
			if(this.element.month) this.element.month.set('value', _date.month);
			if(this.element.year)  this.element.year.set('value', _date.year);
			this.hide();
		} else {
			switch(this.format) {
				case "m/d/y": this.element.set('value', _date.month + this.separator + _date.day + this.separator + _date.year); break;
				case "y/m/d": this.element.set('value', _date.year + this.separator + _date.month + this.separator + _date.day); break;
				case "y/d/m": this.element.set('value', _date.year + this.separator +  _date.day + this.separator + _date.month); break;
				case "d/m/y": default: this.element.set('value', _date.day + this.separator + _date.month + this.separator + _date.year);
			}
			this.hide();
		}
	},

	getInputDate: function(_date) {
		var day, month, year;

		if(_date) {
			day = _date.day;
			month = _date.month;
			year = _date.year;
		} else if(this.separateInput) {
			day = this.element.day.get('value').toInt();
			month = this.element.month.get('value').toInt();
			year = this.element.year.get('value').toInt();
		} else {
			var date = this.element.get('value').split(this.separator);
			if(date.length != 3) return null;
			switch(this.format) {
				case "m/d/y": day = date[1]; month = date[0]; year = date[2]; break;
				case "y/m/d": day = date[2]; month = date[1]; year = date[0]; break;
				case "y/d/m": day = date[1]; month = date[2]; year = date[0]; break;
				case "d/m/y": default: day = date[0]; month = date[1]; year = date[2];
			}
		}

		if( !this.isNumber(day) || !this.isNumber(month) || !this.isNumber(year) ||	day == 0 || month == 0 || year == '0' ||
		    (this.twoDigitYear && year > 99) || (!this.twoDigitYear && year < 1979) || (!this.twoDigitYear && year > 2030) || month > 12 || day > 31 ) return null;

		if(this.twoDigitYear && this.isNumber(year) && year < 100) {
			year = year.toInt();
			if(year < 10) year = '200'+  year;
			else if(year < 70) year = '20'+  year;
			else if(year > 69) year = '19'+  year;
			else year = new Date().getFullYear();
		}

		return day +'/'+ month +'/'+ year;
	},

	//This function is being called on keyup event if linkWithInput is set to true and when a date is picked
	//If the full date is inserted the picker will change itself to that specific date (month view)
	linkedUpdate: function() {
		var _class = this;
		var date = this.getInputDate();
		if(date && this.pickedDate != date) {
			this.u('month', 'gotoPickedDate=1', function() { _class.fade(true) });
			this.pickedDate = date;
		}
	},

	outsideClick: function(_event, _element) {
		var mousePos = this.getMousePos(_event);
		var elementData = _element.getCoordinates();
		return (mousePos.x > elementData.left && mousePos.x < (elementData.left + elementData.width)) &&
			   (mousePos.y > elementData.top  && mousePos.y < (elementData.top + elementData.height)) ? false : true;
	},

	getMousePos: function(_event) {
		if(document.all) {
			return { 'x': window.event.clientX + window.getScrollLeft(),
					 'y': window.event.clientY + window.getScrollTop() };
		} else {
			return { 'x': _event.page['x'],
					 'y': _event.page['y'] };
		}
	},

	isNumber: function(_number) {
		if(_number == '') return false;
		return (_number >= 0) || (_number < 0) ? true : false;
	},

	//Retrieving positition funtions (like getCoordinates, getTop etc) don't seem to return correct values in some situations in mootools 1.2;
	//Opera returns wrong values, IE returns too small values. This function returns the correct coordinates.
	getPos: function(_element) {
		var x, y = 0;
		if(_element.offsetParent) {
			do {
				x += _element.offsetLeft;
				y += _element.offsetTop;
			} while(_element = _element.offsetParent);
		} else if(_element.x) {
			x += _element.x;
			y += _element.y;
		}
		return { 'x': x, 'y': y };
	}
});

/**
*   Updated: 10/28/2008 -- add/fix dropdown on Modal, add styling for selectbox based on current item
*/
LBP.Dropdownlists = new Class({

    Implements: [Events, Options],
    options: {
        wrapperName: 'dropdown',
        injectTo: 'document',
        selectOverlayID: 'select-overlay',
        selectOverlayClassName: 'select-overlay',
        selectBoxClassName: 'select-box',
        selectListClassName: 'select-list',
        selectItemClassName: 'select-list-item',
        selectBoxWidthDiff: 14,
        selectListWidthDiff: -2,
        maxItems: 7,
        modal: false,
        reverseEgr: false,
        itemClick: null,
        itemOver: null,
        itemLeave: null,
        boxClick: null,
        boxEnter: null,
        boxLeave: null
    },

    initialize: function(options){
        this.setOptions(options);
        var selectOverlay = $(this.options.selectOverlayID);
        if (selectOverlay){
            this.selectOverlay = selectOverlay;
        }
        else {
            this.selectOverlay = new Element(
                'div',
                {
                    'class': this.options.selectOverlayClassName + ' ' + this.options.selectOverlayClassName + '-disabled',
                    'id': this.options.selectOverlayID
                }
            ).inject(document.body);

            this.selectOverlay.addEvent('click', function(){
                this.selectOverlay.addClass(this.options.selectOverlayClassName + '-disabled');
                $$('.' + this.options.selectBoxClassName).each(function(theBox){
                    theBox.removeClass(this.options.selectBoxClassName + '-focus');
                }.bind(this));

                $$('.' + this.options.selectListClassName).each(function(theList){
                    theList.addClass( this.options.selectListClassName + '-disabled');
                }.bind(this));

            }.bind(this));
        }

        $$('.' + this.options.wrapperName).each(function(theDiv){
            if (!theDiv.retrieve('installed')){
                this.build(theDiv);
                theDiv.store('installed', true);
            }
        }.bind(this));
    },

    reposition: function(){
        var size = document.getSize();
        var scroll = document.getScroll();
        this.selectOverlay.setStyles({
            left: scroll.x + 'px',
            top: scroll.y + 'px',
            width: size.x + 'px',
            height: size.y + 'px'
        });
    },

    build: function(wrapperDiv){
        var selector = wrapperDiv.getElement('select');
        selector.setStyle('display', 'none');

        /**
        *    build the box to insert the current text into
        */
        var selectBox = new Element("div",{ 'class': this.options.selectBoxClassName }).inject(wrapperDiv);
        var selectBoxInner = new Element("div").inject(selectBox);
        selectBoxInner.set({'html': selector.options[selector.selectedIndex].text, 'class': this.options.selectBoxClassName + '-inner '});

        /**
        *    build the list to inject all our current values
        */
        var selectList = new Element('div',{ 'class': this.options.selectListClassName + ' ' + this.options.selectListClassName + '-disabled'});
        if(this.options.injectTo == 'document') {
            selectList.inject(document.body);
        } else {
            selectList.inject(wrapperDiv);
        }
        //inject to wrapperDiv instead of document.body


        var maxLength = 0;
        for (var i=0; i < selector.options.length; i++){
            var selectItemInner = new Element(
                'div',{
                    'class': this.options.selectItemClassName + '-inner'
                }
            );

            var selectItem = new Element(
                'div',{
                    'class': this.options.selectItemClassName + ' '+ this.options.selectItemClassName+selector.options[i].value,
                    'id': selector.options[i].value
                }
            ).inject(selectList);

            selectItemInner.inject(selectItem);
            selectItemInner.set('html', selector.options[i].text);

            selectItem.addClass(selector.options[i].className);
            var addMore = (selectItem.getStyle('padding-left').toInt() / 3).toInt();

            this.addItemEvents(selectItem, selectBox, selectList);

            if (maxLength < selector.options[i].text.length + addMore){
                maxLength = selector.options[i].text.length + addMore;
            }

            if (selector.options[i].selected){
                selectItem.addClass(this.options.selectItemClassName + '-current');
            }
        }

        /**
        *    set width for the select box
        */
        var toWidth = wrapperDiv.get('toWidth');
        if(!toWidth || toWidth =='') {
            var toPx = maxLength <= 2 ? maxLength * 2 + 2 : maxLength * 7 + 2;
        } else {
            var toPx = toWidth.toInt();
            wrapperDiv.removeProperty('toWidth');
        }
        selectBox.set(
            'styles',
            {
                'width': toPx + this.options.selectBoxWidthDiff
            }
        );

        this.addBoxEvents(selectBox, selectList);
        if(this.options.reverseEgr) {
            selector.store('selectBox', selectBox);
            selector.store('selectList', selectList);
            selector.addEvent('onReverse', function(selector, idx){
                this.reverseCurrent(selector, idx);
            }.bind(this));
        }
    },

    //===========================================================================
    // events for box
    //===========================================================================

    addBoxEvents: function(selectBox, selectList){
        selectBox.addEvent('click', function(){
            if (selectBox.hasClass(this.options.selectBoxClassName + '-disabled')) return;

            selectList.toggleClass( this.options.selectListClassName + '-disabled');
            selectBox.toggleClass( this.options.selectBoxClassName + '-focus');

            /**
            *    calculate the height for the number of items we must show
            */
            var firstItem = selectList.getElement('.' + this.options.selectItemClassName);
            var fipos = firstItem.getSize();
            var maxHeight = fipos.y * this.options.maxItems;

            /**
            *    set the max height if we could :D
            */
            var xpos = selectList.getCoordinates();

            var hasScroll = false;
            if (xpos.height > maxHeight){
                hasScroll = true;
            }

            if (hasScroll){
                if (Browser.Engine.trident){
                    selectList.setStyle('height', maxHeight);
                }
                else {
                    selectList.setStyle('height', maxHeight);
                }
            }

            /**
            *    set width, top and left
            */
            var scrollWidth = 0;
            if (hasScroll){
                scrollWidth = 8;
            }

            var pos = selectBox.getCoordinates();
            if(this.options.modal && this.options.injectTo == 'wrapper') {
                var ptemp = selectBox.getCoordinates($('modal-win-wrapper'));
                pos.top = ptemp.top;
                pos.left = ptemp.left;
            }
            selectList.set(
                'styles', {
                    'width': pos.width + this.options.selectListWidthDiff + scrollWidth,
                    'top': pos.top + pos.height,
                    'left': pos.left
                }
            );

            /**
            *    recalculate the overlay
            */
            if (!selectList.hasClass( this.options.selectListClassName + '-disabled')){
                $(this.options.selectOverlayID).removeClass(this.options.selectOverlayClassName + '-disabled');
                this.reposition();
            }
            (this.options.boxClick || this.boxClick).call(this, selectBox, selectList);
        }.bind(this));

        selectBox.addEvent('mouseenter', function(){
            if (selectBox.hasClass(this.options.selectBoxClassName + '-disabled')) return;
            selectBox.addClass(this.options.selectBoxClassName + '-hover');
            (this.options.boxEnter || this.boxEnter).call(this, selectBox, selectList);
        }.bind(this));

        selectBox.addEvent('mouseleave', function(){
            if (selectBox.hasClass(this.options.selectBoxClassName + '-disabled')) return;
            selectBox.removeClass(this.options.selectBoxClassName + '-hover');
            (this.options.boxLeave || this.boxLeave).call(this, selectBox, selectList);
        }.bind(this));
    },

    //===========================================================================
    // events for items
    //===========================================================================

    addItemEvents: function(selectItem, selectBox, selectList){
        selectItem.addEvent('mouseover', function(){
            selectItem.addClass(this.options.selectItemClassName + '-hover');
            (this.options.itemOver || this.itemOver).call(this, selectItem, selectBox, selectList);
        }.bind(this));

        selectItem.addEvent('mouseleave', function(){
            selectItem.removeClass(this.options.selectItemClassName + '-hover');
            (this.options.itemLeave || this.itemLeave).call(this, selectItem, selectBox, selectList);
        }.bind(this));

        selectItem.addEvent('click', function(){
            this.setCurrent(selectItem, selectBox, selectList);
            (this.options.itemClick || this.itemClick).call(this, selectItem, selectBox, selectList);
        }.bind(this));
    },

    setCurrent: function(selectItem, selectBox, selectList){
        selector = selectBox.getPrevious('select');
        selector.value = selectItem.id;
        var selectBoxInner = selectBox.getElement('.' + this.options.selectBoxClassName + '-inner');
        selectBoxInner.set({'html': selectItem.get('html'), 'class' : this.options.selectBoxClassName + '-inner '+this.options.selectItemClassName+selectItem.get('id')});
        selectList.addClass( this.options.selectListClassName + '-disabled');
        this.selectOverlay.addClass(this.options.selectOverlayClassName + '-disabled');
        selectBox.removeClass(this.options.selectBoxClassName + '-focus');
        selector.fireEvent('change');
        selectList.getElements('.' + this.options.selectItemClassName).each(function(singleItem){
            singleItem.removeClass(this.options.selectItemClassName + '-current');
        }.bind(this));
        selectItem.addClass(this.options.selectItemClassName + '-current');
    },

    reverseCurrent: function(selector, idx) {
        var selectBox = selector.retrieve('selectBox');
        var selectList = selector.retrieve('selectList');
        var selectItem = selectList.getElements('.' + this.options.selectItemClassName + idx);
        var selectBoxInner = selectBox.getElement('.' + this.options.selectBoxClassName + '-inner');
        selector.value = selectItem.id;
        selectBoxInner.set('html', selectItem.get('html'));
        selector.fireEvent('change');
        selectList.getElements('.' + this.options.selectItemClassName).each(function(singleItem){
            singleItem.removeClass(this.options.selectItemClassName + '-current');
        }.bind(this));
        selectItem.addClass(this.options.selectItemClassName + '-current');
    },
    itemClick: function(){},
    itemOver: function(){},
    itemLeave: function(){},
    boxClick: function(){},
    boxEnter: function(){},
    boxLeave: function(){}

});

LBP.Editor = new Class({
    Implements: [Events, Options],
	options:{
		styleWithCSS: 	false,
        CSSFileURL: 	'http://static.acc.vn/styles/controls/editor_body.css',
        use_format:		false,
		use_fontface:	false,
		use_fontsize:	false,
		use_image:		false,
		use_color:		false,
		use_list:		false,
		use_align:		false,
		use_float:		false,
		use_resize:		false,
		use_count:		false,
		use_help:		false,
		help_link:		'http://example.acconline.vn',
		help_caller:	'browser',
		max_length:		null,

        emoBoardOverlayID: 'emoboard-overlay',
		emoBoardOverlayClassName: 'emoboard-overlay',
		emoListId: 'emolist',
		emoListClassName: 'emolist'
	},

	id: 'mooeditable',
	initialize: function(element, options){
		this.setOptions(options);
		var thisElement = $(element);
		if (thisElement && !thisElement.retrieve('hasEditor')){
			thisElement.store('hasEditor', true);
			this.build(thisElement);
		}
	},

	build: function(el){
		// init
		var className = el.className.split(' ')[0];
		var id = el.id ? el.id : 'mooeditable';
        var elStyles = el.getStyles('width','height','padding','margin','border','border-width');
		var sides = ['top','right','bottom','left'];
		for (var i = 0; i < sides.length; i++) {
			elStyles['padding-' + sides[i]] = elStyles['padding'].split(' ')[i];
			elStyles['border-' + sides[i] + '-width'] = elStyles['border-width'].split(' ')[i];
		}
		$( id + '-container').removeClass('not-ready');
		el.removeClass('not-ready');
		var elpos = el.getCoordinates();
        var iframeWidth = elpos.width;
		var iframeHeight = elpos.height;

		// store the id into main class
		this.id = id;

        // Build the container
		var container = new Element(
			'div'
		);

		// Put textarea inside container
		container.inject(el, 'before');
		el.setStyles({
			'margin': 0,
			'display': 'none',
			'resize': 'none', // disable resizable textareas in Safari
			'outline': 'none' // disable focus ring in Safari
		});
		if(Browser.Engine.trident){
			// Fix IE bug, refer "IE/Win Inherited Margins on Form Elements" <http://positioniseverything.net/explorer/inherited_margin.html>
			var span = new Element('span');
			span.inject(container, 'bottom');
			el.inject(span, 'bottom');
		}
		else el.inject(container, 'bottom');

		// Build the iframe
		var iframe = new IFrame({
			'class': className + '-iframe',
			'id': id + '-iframe',
			'frameborder': 0
		});
		iframe.inject(el, 'after');

		// Build the content of iframe, remember that we can add a css file for styles inside the doc body
		var doc = iframe.contentWindow.document;
		doc.open();
        doc.write('<!DOCTYPE html PUBLIC "-/W3C/DTD XHTML 1.1/EN" "http:/www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">');
        doc.write('<html xmlns="http:/www.w3.org/1999/xhtml" xml:lang="en" style="cursor: text; height: 100%">');

        if (Browser.Engine.gecko && Browser.Engine.version < 19){
        	doc.write('<body id="editable">');
        	doc.write("<link href=\"" + this.options.CSSFileURL + "\" type=\"text/css\" rel=\"stylesheet\"/>" );
		}
		else {
	        doc.write("<head>");
	        doc.write("<link href=\"" + this.options.CSSFileURL + "\" type=\"text/css\" rel=\"stylesheet\"/>" );
	        doc.write("</head>");
	        doc.write('<body id="editable">');
		}

		doc.write('</body>');
		doc.write('</html>');
		doc.close();

		// we will set the iframe to the textarea's value ^^
		doc.body.innerHTML = el.value;

        // Turn on Design Mode
        if (Browser.Engine.trident){
        	doc.body.contentEditable = true
		}
		else {
        	doc.designMode = 'on';
		}

		// Update the event for textarea's corresponding labels, in most case, we dont have this
		if(el.id) $$('label[for="'+el.id+'"]').addEvent('click', function(e){
			if(iframe.getStyle('display') != 'none'){
				e = new Event(e).stop();
				iframe.contentWindow.focus();
			}
		});

		// Ensures textarea content is always updated
		var events = ['mousedown', 'mouseup', 'mouseout', 'mouseover', 'click', 'keydown', 'keyup', 'keypress'];
		for(var i = 0; i < events.length; i++){
			if($type(document.addEventListener) == 'function'){
				doc.addEventListener(events[i], function(oEvent){
					el.value = this.cleanup(doc.getElementById('editable').innerHTML);
				}.bind(this), false);
			}
			else{
				doc.attachEvent('on' + events[i], function(){
					el.value = this.cleanup(doc.getElementById('editable').innerHTML);
				}.bind(this));
			}
		}

		this.initButtons(el, iframe, id, className);

		// turn on everything
		$( id + '-wrapper' ).removeClass('not-installed');
        $( id + '-toolbar').removeClass('not-ready');
        if (this.options.use_resize){
        	$( id + '-resizer').removeClass('not-ready');
        	var thisEditor = this;
	        var editorResize = new Drag(id + '-resizer', {
			    snap: 0,
			    limit: { y: [0, 300] },
			    modifiers: { x: 'scrollLeft' },
			    onSnap: function(el){
			        el.addClass('editor-resizer-dragging');
			    },
			    onComplete: function(el){
			        el.removeClass('editor-resizer-dragging');
			        if (!this.lastValue){
        				var diff = this.value.now.y;
        				this.lastValue = this.value.now.y;
					}
					else {
						var diff = this.value.now.y - this.lastValue;
						this.lastValue = this.value.now.y;
					}
					thisEditor.changeHeight(diff);
					el.setStyle('top', 0);
					this.lastValue = 0;
			    }
			});
		}
	},

	initButtons: function(el, iframe, id, className)
	{
		var toolbar = $( id + '-toolbar');

        if (this.options.use_color)
	    {
	    	var localThis = this;
		    new LBP.ColorPicker(
		        id + '-textcolor',
		        {
		        	'id': id + '-textcolorpicker',
		            'startColor': [0, 0, 0],
		            'onChange': function(color) {
		                localThis.action('textcolor', el, iframe, toolbar, color.hex);
		            }
		        }
		    );

		    new LBP.ColorPicker(
		        id + '-highlightcolor',
		        {
		            'id': id + 'hilitecolorpicker',
		            'startColor': [0, 0, 0],
		            'onChange': function(color) {
		                localThis.action('highlightcolor', el, iframe, toolbar, color.hex);
		            }
		        }
		    );
		}

        if (this.options.use_fontface)
	    {
	    	var fontfaceButton = $( id + '-fontface');
            fontfaceButton.addEvent('change', function(){
                if (fontfaceButton.value != 'none')
		    	{
		        	this.action('fontface', el, iframe, toolbar, fontfaceButton.value);
				}
		        iframe.contentWindow.focus();
		    }.bind(this));
		}

        if (this.options.use_fontsize)
	    {
	    	var fontsizeButton = $( id + '-fontsize');
        	fontsizeButton.addEvent('change', function(){
                if (fontsizeButton.value != 'none')
		    	{
		        	this.action('fontsize', el, iframe, toolbar, fontsizeButton.value);
				}
		        iframe.contentWindow.focus();
		    }.bind(this));
		}

        if (this.options.use_format)
	    {
	    	var formatButton = $( id + '-format');
		    formatButton.addEvent('change', function(){
		    	if (formatButton.value != 'none')
		    	{
		        	this.action('format', el, iframe, toolbar, formatButton.value);
				}
		        iframe.contentWindow.focus();
		    }.bind(this));
		}

		var buttons = toolbar.getElements('.toolbar-button');
        buttons.each(function(item){
			item.addEvent('click', function(e){
				e = new Event(e).stop();
				if (!item.hasClass('button-disabled') && !item.hasClass('toolbar-color-button')){
					var command = item.className.split(' ')[0].split('-')[1];
					if (command === 'countchar'){
						this.countChar();
					}
					else if (command === 'help'){
                        var tempRequest = new Request.LBP({
							'url':	this.options.help_link
						}).send('sendby=' + this.options.help_caller);
					}
					else if (command == 'insertemo'){
						this.createEmoBoard(id, item, el, iframe, toolbar);
					}
					else {
						this.action(command, el, iframe, toolbar);
					}
				}
				iframe.contentWindow.focus();
			}.bind(this));

			// remove focus rings off the buttons in Firefox
			item.addEvent('mousedown', function(e){ new Event(e).stop(); });

			// add hover effect for IE6
			if(Browser.Engine.trident4) item.addEvents({
				'mouseenter': function(e){ this.addClass('toolbar-button-hover'); },
				'mouseleave': function(e){ this.removeClass('toolbar-button-hover'); }
			});

		}.bind(this));
	},

    pasteHTML: function(doc, iframe, start, end)
    {
    	iframe.contentWindow.focus();
        if (Browser.Engine.trident)
        {
            var sel = doc.selection.createRange();
            var html = start + sel.htmlText + end;
            sel.pasteHTML(html);
        }
        else
        {
            selection = iframe.contentWindow.window.getSelection();
            if (selection) {
                range = selection.getRangeAt(0);
            } else {
                range =  doc.createRange();
            }
            var html = start + selection + end;
            var fragment = doc.createDocumentFragment();
            var div = doc.createElement("div");
            div.innerHTML = html;
            while (div.firstChild) {
            	fragment.appendChild(div.firstChild);
            }
            selection.removeAllRanges();
            range.deleteContents();
            var node = range.startContainer;
            var pos = range.startOffset;
            switch (node.nodeType) {
            case 3:
                if (fragment.nodeType == 3) {
                    try
                    {
                        node.insertData(pos, fragment.data);
                        range.setEnd(node, pos + fragment.length);
                        range.setStart(node, pos + fragment.length);
                    }
                    catch(e){}
                }
                else
                {
                    try
                    {
                        node = node.splitText(pos);
                        node.parentNode.insertBefore(fragment, node);
                        range.setEnd(node, pos + fragment.length);
                        range.setStart(node, pos + fragment.length);
                    }
                    catch(e){}
                }
                break;

            case 1:
                try
                {
                    node = node.childNodes[pos];
                    node.parentNode.insertBefore(fragment, node);
                    range.setEnd(node, pos + fragment.length);
                    range.setStart(node, pos + fragment.length);
                    break;
                }
                catch(e){}
            }
            selection.addRange(range);
        }
    },

	action: function(command, el, iframe, toolbar, extra){
		var win = iframe.contentWindow;
		var doc = win.document;
		var selection = '';
		switch(command){
			case 'createlink':
				if (doc.selection){
					selection = doc.selection.createRange().text;
					if (selection == ''){
						alert("Please select the text you wish to hyperlink.");
						break;
					}
				}
				else{
					selection = win.getSelection();
					if (selection == ''){
						alert("Please select the text you wish to hyperlink.");
						break;
					}
				}
				var url = prompt('Enter URL','http://');
				if (url !== null) this.execute(el, doc, command, false, url);
				break;
            case 'insertimage':
				var url = prompt('Enter Image URL','http://');
				if (url !== null) this.execute(el, doc, command, false, url);
				break;
            case 'textcolor':
                if (!Browser.Engine.trident) this.execute(el, doc, 'styleWithCSS', false, this.options.styleWithCSS);
                this.execute(el, doc, "forecolor", false, extra);
                break;
            case 'highlightcolor':
                //if (!Browser.Engine.trident) this.execute(el, doc, 'styleWithCSS', false, this.options.styleWithCSS);
				if (!Browser.Engine.trident) this.execute(el, doc, 'styleWithCSS', false, true);
                if (!Browser.Engine.trident)
					this.execute(el, doc, "hilitecolor", false, extra);
                else
					this.execute(el, doc, "backcolor", false, extra);
                break;
            case 'fontface':
                if (!Browser.Engine.trident) this.execute(el, doc, 'styleWithCSS', false, this.options.styleWithCSS);
                this.execute(el, doc, "fontname", false, extra);
                break;
            case 'fontsize':
                if (!Browser.Engine.trident) this.execute(el, doc, 'styleWithCSS', false, this.options.styleWithCSS);
                this.execute(el, doc, "fontsize", false, extra);
                break;
            case 'format':
                if (!Browser.Engine.trident) this.execute(el, doc, 'styleWithCSS', false, this.options.styleWithCSS);
                if (Browser.Engine.trident) extra = '<' + extra + '>';
                this.execute(el, doc, "formatblock", false, extra);
                break;
			case 'toggleview':
				this.toggle(el,iframe,toolbar);
				break;
			case 'formatquote':
				this.pasteHTML(doc, iframe, "<div class='quotemain pkg'>", "</div>");
				break;
            case 'formatfloatleft':
				this.pasteHTML(doc, iframe, "<div class='float-left'>", "</div>");
				break;
            case 'formatfloatright':
				this.pasteHTML(doc, iframe, "<div class='float-right'>", "</div>");
				break;
			default:
				if (!Browser.Engine.trident) this.execute(el, doc, 'styleWithCSS', false, this.options.styleWithCSS);
				this.execute(el, doc, command, false, '');
		}
	},

	execute: function(el, doc, command, param1, param2){
	    if (!this.busy){
			this.busy = true;
			doc.execCommand(command, param1, param2);
			el.value = this.cleanup(doc.getElementById('editable').innerHTML);
			this.busy = false;
		}
		return false;
	},

	toggle: function(el,iframe,toolbar) {
		if (iframe.getStyle('display') == 'none') {
			iframe.setStyle('display', '');
			var doc = iframe.contentWindow.document;
			(function(){
			doc.body.innerHTML = el.value;
			}).delay(1); // dealing with Adobe AIR's webkit bug
			toolbar.getElements('.toolbar-button').each(function(item){
				item.set('disabled', false);
				item.removeClass('toolbar-button-disabled');
				item.set('opacity', 1);
			});
            toolbar.getElements('.toolbar-select').each(function(item){
				item.set('disabled', false);
			});

            toolbar.getElements('.ed-select-box').each(function(item){
				item.removeClass('ed-select-box-disabled');
				item.set('opacity', 1);
			});

			el.setStyle('display', 'none');
		} else {
			el.setStyle('display', '');
			var doc = iframe.contentWindow.document;
			el.value = this.cleanup(doc.body.innerHTML);
			toolbar.getElements('.toolbar-button').each(function(item){
				if (!item.hasClass('editor-toggleview')) {
					item.set('disabled', true);
					item.addClass('toolbar-button-disabled');
					item.set('opacity', 0.4);
				}
			});

            toolbar.getElements('.toolbar-select').each(function(item){
				item.set('disabled', true);
			});

			toolbar.getElements('.ed-select-box').each(function(item){
				item.addClass('ed-select-box-disabled');
				item.set('opacity', 0.4);
			});

			iframe.setStyle('display', 'none');
			el.focus();
		}
	},

	cleanup: function(source){
		// make the br and </p> newline for good view
		source = source.replace(/<br>[\s]*/g,"<br />\n");
		source = source.replace(/<br \/>[\s]*/g,"<br />\n");
		source = source.replace(/<\/p>[\s]*/g,"</p>\n");

		// Remove leading and trailing whitespace
		source = source.replace(/^\s+/, '');
		source = source.replace(/\s+$/, '');

		// Remove trailing BRs
		source = source.replace(/<br>$/, '');

		// Remove BRs right before the end of blocks
		source = source.replace(/<br>\s*<\/(h1|h2|h3|h4|h5|h6|li|p)/gi, '</$1');

		// Remove empty tags
		// source = source.replace(/(<[^\/]>|<[^\/][^>]*[^\/]>)\s*<\/[^>]*>/gi, '');

		// Webkit cleanup
		source = source.replace(/ class=\"Apple-style-span\"/gi, '');
		source = source.replace(/<SPAN style=\"\">/gi, '');

		// Replace uppercase element names with lowercase
		source = source.replace(/<[^> ]*/g, function(match){return match.toLowerCase();});

		// Replace uppercase attribute names with lowercase
		source = source.replace(/<[^>]*>/g, function(match){
			match = match.replace(/ [^=]+=/g, function(match2){return match2.toLowerCase();});
			return match;
		});

		// Put quotes around unquoted attributes
		source = source.replace(/<[^>]*>/g, function(match){
			match = match.replace(/( [^=]+=)([^"][^ >]*)/g, "$1\"$2\"");
			return match;
		});

		return source;
	},

    countChar: function(){
    	if (this.options.max_length){
    		var charLeft = this.options.max_length - $(this.id).value.length;
			alert('Bạn còn ' + charLeft + ' ký tự HTML có thể sử dụng');
		}
		else {
			alert('Bạn đã dùng hết ' + $(this.id).value.length + ' ký tự HTML');
		}
	},

    changeHeight: function(diff)
	{
		if ($(this.id).getStyle('height').toInt() < 800)
		{
			$(this.id).setStyle('height', $(this.id).getStyle('height').toInt() + diff);
			$(this.id + '-iframe').setStyle('height', $(this.id + '-iframe').getStyle('height').toInt() + diff);
		}
	},

	createEmoBoard: function(id, item, el, iframe, toolbar){
        var emoBoardOverlay = $(id + this.options.emoBoardOverlayID);
		if (emoBoardOverlay){
			this.emoBoardOverlay = emoBoardOverlay;
		}
		else {
			this.emoBoardOverlay = new Element(
				'div',
				{
					'class': this.options.emoBoardOverlayClassName,
					'id': id + this.options.emoBoardOverlayID
				}
			).inject(document.body);

			this.emoBoardOverlay.addEvent('click', function(){
				/* remove the overlay */
				this.emoBoardOverlay.addClass(this.options.emoBoardOverlayClassName + '-disabled');
				/* remove the list */
				this.emoList.addClass(this.options.emoListClassName + '-disabled');
			}.bind(this));
		}

		this.emoBoardOverlay.removeClass(this.options.emoBoardOverlayClassName + '-disabled');
		this.createEmoList(id, item, el, iframe, toolbar);
		this.reposition(item);
	},

	createEmoList: function(id, item, el, iframe, toolbar){
		var emoList = $(id + this.options.emoListId);
		if (emoList){
			this.emoList = emoList;
			this.emoList.removeClass(this.options.emoListClassName + '-disabled');
		}
		else {
			this.emoList = new Element(
				"div",
				{
					'id': id + this.options.emoListId,
					'class': this.options.emoListClassName
				}
			).inject(document.body);

            this.emoList.set(
				'html',
				"<div class='smilies pkg'><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/16.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/15.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/41.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/39.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/31.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/11.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/47.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/65.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/19.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/22.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/17.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/21.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/37.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/43.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/52.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/23.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/51.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/49.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/57.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/79.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/48.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/29.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/54.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/09.gif'></div><div class='smiley'><img src='http://accvietnam.vn/App_Resource/html/emoticons/08.gif'></div></div>"
			);

            var win = iframe.contentWindow;
			var doc = win.document;

			this.emoList.getElements('img').each(function(emo){
				emo.addEvent(
					'click',
					function(){
						this.execute(
							el,
							doc,
							'insertimage',
							false,
							emo.get('src')
						);
					}.bind(this));
			}.bind(this));
		}
	},

    reposition: function(item){
    	var cor = item.getCoordinates();
		var size = document.getSize();
        var scroll = document.getScroll();
		this.emoBoardOverlay.setStyles({
			left: scroll.x + 'px',
			top: scroll.y + 'px',
			width: size.x + 'px',
			height: size.y + 'px'
		});

		this.emoList.setStyles({
            'top': cor.top + 'px',
			'left': cor.left + 'px'
		})
	}
});

/*
Script: FixPNG.js
	Extends the Browser hash object to include methods useful in managing the window location and urls.

License:
	http://clientside.cnet.com/wiki/cnet-libraries#license
*/
$extend(Browser, {
	fixPNG: function(el) {
		try {
			if (Browser.Engine.trident4){
				el = $(el);
				if (!el) return el;
				if (el.get('tag') == "img" && el.get('src').test(".png")) {
					var vis = el.isVisible();
					try { //safari sometimes crashes here, so catch it
						dim = el.getSize();
					}catch(e){}
					if(!vis){
						var before = {};
						//use this method instead of getStyles
						['visibility', 'display', 'position'].each(function(style){
							before[style] = this.style[style]||'';
						}, this);
						//this.getStyles('visibility', 'display', 'position');
						this.setStyles({
							visibility: 'hidden',
							display: 'block',
							position:'absolute'
						});
						dim = el.getSize(); //works now, because the display isn't none
						this.setStyles(before); //put it back where it was
						el.hide();
					}
					var replacement = new Element('span', {
						id:(el.id)?el.id:'',
						'class':(el.className)?el.className:'',
						title:(el.title)?el.title:(el.alt)?el.alt:'',
						styles: {
							display: vis?'inline-block':'none',
							width: dim.x,
							height: dim.y,
							filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader (src='"
								+ el.src + "', sizingMethod='scale');"
						},
						src: el.src
					});
					if(el.style.cssText) {
						try {
							var styles = {};
							var s = el.style.cssText.split(';');
							s.each(function(style){
								var n = style.split(':');
								styles[n[0]] = n[1];
							});
							replacement.setStyle(styles);
						} catch(e){ dbug.log('fixPNG1: ', e)}
					}
					if(replacement.cloneEvents) replacement.cloneEvents(el);
					replacement.replaces(el);
				} else if (el.get('tag') != "img") {
				 	var imgURL = el.getStyle('background-image');
				 	if (imgURL.test(/\((.+)\)/)){
				 		el.setStyles({
				 			background: '',
				 			filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled='true', sizingMethod='crop', src=" + imgURL.match(/\((.+)\)/)[1] + ")"
				 		});
				 	};
				}
			}
		} catch(e) {dbug.log('fixPNG2: ', e)}
	},
  pngTest: /\.png$/, // saves recreating the regex repeatedly
  scanForPngs: function(el, className) {
    className = className||'fixPNG';
    //TODO: should this also be testing the css background-image property for pngs?
    //Q: should it return an array of all those it has tweaked?
    if (document.getElements){ // more efficient but requires 'selectors'
      el = $(el||document.body);
      el.getElements('img[src$=.png]').addClass(className);
    } else { // scan the whole page
      var els = $$('img').each(function(img) {
        if (Browser.pngTest(img.src)){
          img.addClass(className);
        }
      });
    }
  }
});
if(Browser.Engine.trident4) window.addEvent('domready', function(){$$('img.fixPNG').each(Browser.fixPNG)});


/*
Script: Element.Shortcuts.js
	Extends the Element native object to include some shortcut methods.

License:
	http://clientside.cnet.com/wiki/cnet-libraries#license
*/

Element.implement({
	isVisible: function() {
		return this.getStyle('display') != 'none';
	},
	toggle: function() {
		return this[this.isVisible() ? 'hide' : 'show']();
	},
	hide: function() {
		var d;
		try {
			//IE fails here if the element is not in the dom
			if ('none' != this.getStyle('display')) d = this.getStyle('display');
		} catch(e){}
		this.store('originalDisplay', d||'block');
		this.setStyle('display','none');
		return this;
	},
	show: function(display) {
		original = this.retrieve('originalDisplay')?this.retrieve('originalDisplay'):this.get('originalDisplay');
		this.setStyle('display',(display || original || 'block'));
		return this;
	},
	swapClass: function(remove, add) {
		return this.removeClass(remove).addClass(add);
	},
	//TODO
	//DO NOT USE THIS METHOD
	//it is temporary, as Mootools 1.1 will negate its requirement
	fxOpacityOk: function(){
		return !Browser.Engine.trident4;
	}
});

/**
 * FancyUpload - Flash meets Ajax for powerful and elegant uploads.
 *
 * @version		2.1
 *
 * @license		MIT License
 *
 * @author		Harald Kirschner <mail [at] digitarald [dot] de>
 * @copyright	Authors
 */

LBP.FileUploader = new Class({

	Extends: Swiff.Uploader,

	options: {
		limitSize: false,
		limitFiles: 5,
		instantStart: false,
		allowDuplicates: false,
		validateFile: $lambda(true), // provide a function that returns true for valid and false for invalid files.
		debug: false,
		fileInvalid: null, // called for invalid files with error stack as 2nd argument
		fileCreate: null, // creates file element after select
		fileComplete: null, // updates the file element to completed state and gets the response (2nd argument)
		fileRemove: null, // removes the element
		fileUpload: null
		/*fileUpload: function() {
			var v = new Hash({
				'url': this.options.newUrl
			}).combine(this.options);
			return v;
		}*/
		/**
		 * Events:
		 * onBrowse, onSelect, onAllSelect, onCancel, onBeforeOpen, onOpen, onProgress, onComplete, onError, onAllComplete
		 */
	},

	initialize: function(status, list, options) {

		this.status = $(status);
		this.list = $(list);

		this.files = [];

		if (options.callBacks) {
			this.addEvents(options.callBacks);
			options.callBacks = null;
		}

		this.parent(options);
		this.render();
	},

	render: function() {
		this.overallTitle = this.status.getElement('.overall-title');
		this.currentTitle = this.status.getElement('.current-title');
		this.currentText = this.status.getElement('.current-text');

		var progress = this.status.getElement('.overall-progress');
		this.overallProgress = new Fx.ProgressBar(progress, {
			text: new Element('span', {'class': 'progress-text'}).inject(progress, 'after')
		});
		progress = this.status.getElement('.current-progress')
		this.currentProgress = new Fx.ProgressBar(progress, {
			text: new Element('span', {'class': 'progress-text'}).inject(progress, 'after')
		});
	},

	onLoad: function() {
	},

	onBeforeOpen: function(file, options) {
		var v = new Hash({
			'url': this.options.newUrl
		}).combine(options);
		return v;
		/*var fn = this.options.fileUpload;
		var obj = (fn) ? fn.call(this, this.getFile(file), options) : options;
		return obj;*/
	},

	onOpen: function(file, overall) {
		file = this.getFile(file);
		file.element.addClass('file-uploading');
		this.currentProgress.cancel().set(0);
		this.currentTitle.set('html', 'File Progress "{name}"'.substitute(file) );
	},

	onProgress: function(file, current, overall) {
		this.overallProgress.start(overall.bytesLoaded, overall.bytesTotal);
		this.currentText.set('html', 'Đang tải lên với tốc độ {rate}/s. Thời gian còn lại: ~{timeLeft}'.substitute({
			rate: (current.rate) ? this.sizeToKB(current.rate) : '- B',
			timeLeft: Date.fancyDuration(current.timeLeft || 0)
		}));
		this.currentProgress.start(current.bytesLoaded, current.bytesTotal);
	},

	onSelect: function(file, index, length) {
		var errors = [];
		if (this.options.limitSize && (file.size > this.options.limitSize)) errors.push('size');
		if (this.options.limitFiles && (this.countFiles() >= this.options.limitFiles)) errors.push('length');
		if (!this.options.allowDuplicates && this.getFile(file)) errors.push('duplicate');
		if (!this.options.validateFile.call(this, file, errors)) errors.push('custom');
		if (errors.length) {
			var fn = this.options.fileInvalid;
			if (fn) fn.call(this, file, errors);
			return false;
		}
		(this.options.fileCreate || this.fileCreate).call(this, file);
		this.files.push(file);
		return true;
	},

	onAllSelect: function(files, current, overall) {
		this.updateOverall(current.bytesTotal);
		this.status.removeClass('status-browsing');
		if (this.files.length && this.options.instantStart) this.upload.delay(10, this);
	},

	onComplete: function(file, response) {
		this.currentText.set('html', 'Tải lên hoàn tất!');
		this.currentProgress.start(100);
		(this.options.fileComplete || this.fileComplete).call(this, this.finishFile(file), response);
	},

	onError: function(file, error, info) {
		(this.options.fileError || this.fileError).call(this, this.finishFile(file), error, info);
	},

	onCancel: function() {
		this.status.removeClass('file-browsing');
	},

	onAllComplete: function(current) {
		this.updateOverall(current.bytesTotal);
		this.overallProgress.start(100);
		this.status.removeClass('file-uploading');
	},

	browse: function(fileList) {
		var ret = this.parent(fileList);
		if (ret !== true){
		} else {
			this.status.addClass('file-browsing');
		}
	},

	upload: function(options) {
		var ret = this.parent(options);
		if (ret !== true) {
			if (ret) alert(ret);
		} else {
			this.status.addClass('file-uploading');
			this.overallProgress.set(0);
		}
	},

	removeFile: function(file) {
		var remove = this.options.fileRemove || this.fileRemove;
		if (!file) {
			this.files.each(remove, this);
			this.files.empty();
			this.updateOverall(0);
		} else {
			if (!file.element) file = this.getFile(file);
			this.files.erase(file);
			remove.call(this, file);
			this.updateOverall(this.bytesTotal - file.size);
		}
		this.parent(file);
	},

	getFile: function(file) {
		var ret = null;
		this.files.some(function(value) {
			if ((value.name != file.name) || (value.size != file.size)) return false;
			ret = value;
			return true;
		});
		return ret;
	},

	countFiles: function() {
		var ret = 0;
		for (var i = 0, j = this.files.length; i < j; i++) {
			if (!this.files[i].finished) ret++;
		}
		return ret;
	},

	updateOverall: function(bytesTotal) {
		this.bytesTotal = bytesTotal;
		this.overallTitle.set('html', 'Toàn bộ (' + this.sizeToKB(bytesTotal) + ')');
	},

	finishFile: function(file) {
		file = this.getFile(file);
		file.element.removeClass('file-uploading');
		file.finished = true;
		return file;
	},

	fileCreate: function(file) {
		file.info = new Element('span', {'class': 'file-info'});
		file.element = new Element('li', {'class': 'file'}).adopt(
			new Element('span', {'class': 'file-size', 'html': this.sizeToKB(file.size)}),
			new Element('a', {
				'class': 'file-remove',
				'href': '#',
				'html': 'Xóa',
				'events': {
					'click': function() {
						this.removeFile(file);
						return false;
					}.bind(this)
				}
			}),
			new Element('span', {'class': 'file-name', 'html': file.name.length > 30 ? file.name.substr(0, 30) + '...' : file.name}),
			file.info
		).inject(this.list);
	},

	fileComplete: function(file, response) {
		this.defaultFileComplete(file, response);
	},

	defaultFileComplete: function(file, response) {		
		this.options.processResponse || this;
		var json = $H(JSON.decode(response, true));
        var html = json.get('html');
        if (html){
			var realhtml = Base64.decode(html);
			LBP.distributeHTML(realhtml);
			LBP.runProcesses();
			LBP.runProcesses('after');
		}

		if (json.get('result') == 'success') {
			file.element.addClass('file-success');
			file.info.set('html', json.get('size'));

		} else {
			file.element.addClass('file-failed');
			file.info.set('html', json.get('error') || response);
		}

        //var newSessionCode = json.get('session_code');
        //if (newSessionCode){
		    //var pos = this.options.url.indexOf("session_code=");
			//var first = this.options.url.substr(0, pos + 13);
			//this.options.newUrl = first + newSessionCode;
			//LBP.updateSession( newSessionCode );
		//}
	},

	fileError: function(file, error, info) {
		file.element.addClass('file-failed');
		file.info.set('html', '<strong>' + error + '</strong><br />' + info);
	},

	fileRemove: function(file) {
		file.element.fade('out').retrieve('tween').chain(Element.destroy.bind(Element, file.element));
	},

	sizeToKB: function(size) {
		var unit = 'B';
		if ((size / 1048576) > 1) {
			unit = 'MB';
			size /= 1048576;
		} else if ((size / 1024) > 1) {
			unit = 'kB';
			size /= 1024;
		}
		return size.round(1) + ' ' + unit;
	}
});

LBP.iPhoneMenu = new Class({
	initialize: function(menus){
		menus.each(this.renderMenu);
	},
	renderMenu: function(menu){
		if (menu.retrieve('installed')){
			return;
		}
		menu.store('installed', true);

		var items = menu.getElements('.item');
		$each(items, function(current_item){
			var current_link = current_item.getElement('.text a');

			if (current_link){
				current_item.addEvent('click', function(theEvent){
					if (current_link.rev != ''){
						var rh = new lbp_request_handler;
						rh.request_url(current_link.href, 'sendby='+current_link.rev);
					}
					else {
						window.location.href = current_link.href;
					}
				});

				if (!current_item.hasClass('item-current')){
	                current_item.addEvent('mouseover', function(){
						current_item.addClass('item-hover');
					});
			        current_item.addEvent('mouseout', function(){
						current_item.removeClass('item-hover');
					});
				}
			}

			if (current_item.hasClass('item-current')){
				var current_prev = current_item.getPrevious('.item');
				if (current_prev){
					current_prev.addClass('item-current-prev');
				}
			}
		});
	}
});

/**
*/
LBP.MenuDropdown = new Class({
    Implements: [Events, Options],

    options: {
        relativeTo: 'global',
        subItemWidth: 140,
        subItemHeight: 15,
        subItemMargin: 1,
        subItemPadding: 5,
        subCssName: 'submenu',
        subMargin: 1,

        tipMargin: 5,
        padding: 5,
        paddingx: 7,
        paddingy: 4,
        maxSize: { x: 300, y: 30 },
        tipCssName: 'tip',
        opacity: 0.85,
        tipPos: 'top',

        dumper: 'dummydt',

        dropOnHover: false,
        duration: 200,
        idx: 1000
    },

    initialize: function(options)
    {
        this.setOptions(options);
        if (!$(this.options.relativeTo)) return;
    },

    assign: function(el,options)
    {
        this.initialize(options);
        var MenuDropDown = this;
        var subCount = 0;
        if(!this.dump) {
            this.dump = new Element('div', { 'class': 'tip-dump'});
            if(!$(this.options.dumper)){
                $(document.body).adopt(new Element("div",{ id: this.options.dumper}));
            }
            $(this.options.dumper).adopt(this.dump);
            this.dump.set('styles',{ zIndex: -10, position: 'absolute', opacity: 0, display: 'block'});
        }
        el.each( function(item)
        {
            if(!item.retrieve('setMenuTip')) {
            var temp= item.getNext();
            var temp2;
            MenuDropDown.subMenu = null;
            MenuDropDown.subMenu = (temp && (temp.nodeName == 'DIV'))? temp : null;
            if(MenuDropDown.subMenu) {
                subCount++;
                item.removeEvents();
                var arrow = new Element("div", { 'class': 'menu-item-arrow' } );
                var itemTxt = new Element("div", { 'html': item.get('text'), 'class':'menu-item-text' });
                item.empty();
                item.adopt( itemTxt, arrow);
                MenuDropDown.set_up_sub(MenuDropDown.subMenu, item);
                temp2 = MenuDropDown.subMenu;
                item.addEvents({
                    'mouseenter': function(event) {
                        event.stopPropagation();
                        MenuDropDown.set_current(item);
                        MenuDropDown.show_sub(temp2, item);
                    }
                });
                temp2.addEvents({
                    'mouseleave': function(event) {
                        event.stopPropagation();
                        MenuDropDown.hide_sub(temp2);
                    }
                });
                temp2.store('subFx', new Fx.Morph( temp2, { duration: this.options.duration, transition: Fx.Transitions.linear, link: 'cancel', onStart: Events.prototype.clearChain}));
            } else {
                item.addEvent('mouseenter', function(event){
                    if(this.subMenu) {
                        this.hide_sub(this.subMenu);
                    }
                }.bind(this));

            }
            item.store('setMenuTip', true);
            }
        }.bind(this));
        //if(!$(this.options.relativeTo).retrieve('setevents')) {
        $(this.options.relativeTo).addEvent('mouseleave', function(event){
            if(this.subMenu) {
                this.hide_sub(this.subMenu);
            }
        }.bind(this));
        //$(this.options.relativeTo).store('setevents', true);
        //
       //this.dump.destroy();
    },
    set_current: function(item)
    {
        if(this.current) { this.current.removeClass('menu-item-current'); }
        this.current = item;
        this.current.addClass('menu-item-current');
    },
    set_up_sub: function(sub, item)
    {
        var subItems = sub.getChildren();
        item.store('subitemcount', subItems);
        var scroll = document.getScroll();
        var winSize = window.getSize();
        var size = item.getSize();
        subSize = { x: this.options.subItemWidth, y: (this.options.subItemHeight+ this.options.subItemMargin + this.options.subItemPadding *2) * subItems.length };
        sub.set({
            'styles' : {
                'zIndex': this.options.idx,
                'position': 'absolute',
                'width': subSize.x,
                'height': subSize.y,
                'opacity': 0,
                'display': 'none',
                'overflow': 'hidden' },
            'class' : this.options.subCssName
        });
    },

    reposition_sub: function(sub, item){
        var subItems = item.retrieve('subitemcount');

        var scroll = document.getScroll();
        var winSize = window.getSize();
        var size = item.getSize();
        var position = item.getPosition($(this.options.relativeTo));
        var cntrPos = $(this.options.relativeTo).getPosition();
        var subSize = { x: this.options.subItemWidth, y: (this.options.subItemHeight+ this.options.subItemMargin + this.options.subItemPadding *2) * subItems.length };
        if($(this.options.relativeTo).getStyle('position') != 'relative') {
            position.x += cntrPos.x;
            position.y += cntrPos.y;
        }

        if((subSize.y+this.options.subMargin) <= (winSize.y-position.y+scroll.y-size.y)) {
            position.y += (size.y + this.options.subMargin);
        }
        else {
            position.y -=(this.options.subMargin + subSize.y);
        }
        position.x = ((position.x + size.x/2) - (subSize.x/2)).toInt();
        sub.set('styles',{ 'top': position.y,  'left': position.x, 'width': subSize.x, 'height': subSize.y } );
    },
    toggle_sub: function(sub, item)
    {
        if(sub.style.opacity == 0)
        {
            this.show_sub(sub, item);
        }
        else
        {
            this.hide_sub(sub);
        }
    },

    show_sub: function(sub, item)
    {
        if(this.subFx) {
            this.subFx.cancel();
        }
        if(this.isOpen || this.subMenu)
        {
            this.hide_sub(this.subMenu);
        }
        if(this.tipObj && this.tipObj.style.opacity == 1){
            this.tipObj.set('styles', { opacity: 0});
        }
        this.reposition_sub(sub, item);
        this.subFx = sub.retrieve('subFx');
        sub.set('styles', { display: 'block' });
        var to = ({
            opacity: [0, 1],
            width: [this.options.subItemHeight, sub.style.width],
            left: [sub.offsetLeft*4/3, sub.style.left]
        });
        this.subFx.start(to);
        this.isOpen = true;
        this.subMenu = sub;
    },
    hide_sub: function(sub) {
        sub.set('styles', { opacity: 0, display: 'none' });
        if(this.subFx) {
            this.subFx.cancel();
        }
        this.subMenu = null;
        this.isOpen = false;
        if(this.current && this.current.hasClass('menu-item-current')) {
            this.current.removeClass('menu-item-current');
            this.current = null;
        }
    }
});


LBP.MenuTree = new Class ({
    Implements: [Events, Options],
    options: {
        id: '',
        cookieDomain: 'acc.vn',
        cssName: 'menu',
        folderClick: true,
        defaultState: 'close',
        itemClickOpen: false,
        openItems: [],
        dfltCurrentId: '',
        dfltCurrentIdx: -1,
        onUpdate: $empty
    },
    initialize: function(id, options) {
        this.setOptions(options);
        this.options.id = id;
        this.menu = $(id);
        if(!this.menu) return;
        this.setMenuStyle();
    },

    setMenuStyle: function() {
        if(this.menu.retrieve('menutree')) return;
        this.menu.store('menutree', true);
        this.menu.addClass('pmenu-tree');
        this.menuState = new Hash(this.getMenuState());
        var idx = 1;
        this.menu.getElements('li').each(function(item){
            if(!item.retrieve('idxn')){
                item.store('idxn', idx);
                if(this.options.openItems.contains(idx)) item.store('item_state', 'open');
                idx++;
            }
            var last_node = this.isLastNode(item);
            var item_link = item.getFirst('a');
            var has_sub = this.hasSubNode(item_link);
            //if(curr_idxn == item.retrieve('idxn')) this.setCurrent(item_link, item);
            item_link.addClass('tree-item-'+(idx-1));
            var caption = item_link.get('text');
            item_link.addEvent('click', function(event){
                this.setCurrent(item_link, item);
                event.stopPropagation();
            }.bind(this));
            var item_icon = new Element("div", {});
            item_icon.inject(item, 'top');
            if(!has_sub) {
                if(last_node) {
                    item.addClass('node-last');
                    item_icon.addClass('node-last-sign');
                } else {
                    item.addClass('node');
                    item_icon.addClass('node-sign');
                }
                item_link.addClass('menu-link');
            }
            else
            {

                if(last_node) {
                    item.addClass('node-last-fclose');
                    item_icon.addClass('node-last-fsign-close');
                } else {
                    item.addClass('node-fclose');
                    item_icon.addClass('node-fsign-close');
                }
                item_link.addClass('menu-folder');
                var click_item = (this.options.itemClickOpen)? item : item_icon;
                click_item.addEvents({
                    'mouseenter': function(event){
                        item_icon.addClass('node-sign-focus');
                        event.stopPropagation(); },
                    'mouseleave': function(event){
                        if(item_icon.hasClass('node-sign-focus')) { item_icon.removeClass('node-sign-focus'); }
                        event.stopPropagation();
                    },
                    'click':  function(event){
                        this.toggleMenu(event,has_sub, item_link, item, item_icon);
                        this.saveMenuState();
                        event.stopPropagation(); }.bind(this)
                });

                if(!this.options.folderClick) {
                    item_link.removeEvents('click');
                    item_link.removeProperty('href');
                }
                var item_state = $pick( this.menuState.get(item.retrieve('idxn')) , item.retrieve('item_state'),  this.options.defaultState);
                ( item_state == 'close') ? this.closeItem(has_sub, item_link, item, item_icon) : this.openItem(has_sub, item_link, item, item_icon);
            }
        }.bind(this));

        if(this.options.dfltCurrentId != '' && $(this.options.dfltCurrentId)) {
            this.setCurrent($(this.options.dfltCurrentId), $(this.options.dfltCurrentId).getParent());
        } else {
            var curr_idxn = (this.options.dfltCurrent <0)? LBP.getCookie(this.options.id+'current') : this.options.dfltCurrentIdx;
            if(curr_idxn) {
                var idxn = curr_idxn -1;
                var item_link = this.menu.getElements('a.tree-item-'+idxn);
                if(item_link) {
                    this.setCurrent(item_link, item_link.getParent());
                }
            }
        }
        this.onExit();
    },

    update: function() {
    },

    onExit: function() {
        window.addEvent('unload', function(event){ this.saveMenuState(); }.bind(this));
    },

    setCurrent: function(obj, item)
    {
        if(this.current) {
            this.current.removeClass('current');
        }
        this.current = obj;
        LBP.setCookie(this.options.id+'current',item.retrieve('idxn'),7);
        obj.addClass('current');
    },

    toggleMenu: function(event,subItem, item_link, item, item_icon)
    {
        if(subItem.style.display == 'none')
        {
            this.openItem(subItem, item_link, item, item_icon);
            this.menuState.set(item.retrieve('idxn'), 'open');
        }
        else
        {
            this.closeItem(subItem, item_link, item, item_icon);
            this.menuState.set(item.retrieve('idxn'), 'close');
        }
        event.stopPropagation();
    },

    closeItem: function(subItem, item_link, item, item_icon)
    {
        if(item.hasClass('node-fopen'))
        {
            item.removeClass('node-fopen');
            item.addClass('node-fclose');
            if(item_icon.hasClass('node-fsign-open')) item_icon.removeClass('node-fsign-open');
            item_icon.addClass('node-fsign-close');
        }
        else
        {
            if(item.hasClass('node-last-fopen'))
            {
                item.removeClass('node-last-fopen');
                item.addClass('node-last-fclose');
                if(item_icon.hasClass('node-last-fsign-open')) item_icon.removeClass('node-last-fsign-open')
                item_icon.addClass('node-last-fsign-close');
            }
        }
        subItem.style.display = 'none';
    },

    openItem: function (subItem, item_link, item, item_icon)
    {
        if(item.hasClass('node-fclose'))
        {
                item.removeClass('node-fclose');
                item.addClass('node-fopen');
                if(item_icon.hasClass('node-fsign-close')) item_icon.removeClass('node-fsign-close');
                item_icon.addClass('node-fsign-open');
        }
        else
        {
            if(item.hasClass('node-last-fclose'))
            {
                item.removeClass('node-last-fclose');
                item.addClass('node-last-fopen');
                if(item_icon.hasClass('node-last-fsign-close')) item_icon.removeClass('node-last-fsign-close');
                item_icon.addClass('node-last-fsign-open');
            }
        }
        subItem.style.display = 'block';
    },

    isLastNode: function(obj)
    {
        return (obj.getNext())? false: true;
    },

    hasSubNode: function(obj)
    {
        var sub_node = obj.getNext();
        return (sub_node && sub_node.nodeName == 'UL')? sub_node : false;
    },

    getMenuState: function()
    {
        return JSON.decode(LBP.getCookie(this.options.id+'state')) || {};
    },
    saveMenuState: function()
    {
        LBP.setCookie(this.options.id+'state', JSON.encode(this.menuState), 7);
    }
});

/**
*   Updated:    10/01/2008 -- add built-in confirm modal and button support
*               11/11/2008 -- fix/add for modal close and fire Error for ajax handler
*/
LBP.Modal = new Hash({
    presets: {
        drag: false,
        size: { x: 600, y: 400},    //width and height of the content to apply
        headSize: { x: 16, y: 16},
        header: true,
        closeBtn: true,     // if header is false, then no close BTN will be show
        type: '',        // image || iframe || ajax || inline || clone || confirm
        ajaxMarker: '',    //the property to mark a link as ajax, its value will be sent as caller
        cloneMarker:'',     //for a cloned div, its value will be the id of the div to be cloned
        txtMarker: '',
        loadCntr : 'overlay',   //overlay || win
        duration: 60,
/** CSS stuffs below, dont't re-config unless you're using a customized stylesheet & know what you're doing **/
        cssName: 'modal',
        idx: 1200,
        overlay: 'document', // document || caller || none || id
        opacity: 0.8,
        cntPad: [2,2,2,2],  //all cnt padding + border
        headPos: 'top',     //top||bottom||left||right
        headPad: [0,16,2,0],
        winPad: [8,8,8,8]   //all win paddings + borders except for outer most wrapper
    },
    initialize: function(options) {
        if(this.container || this.options) return;
        this.options = {};
        options = options || {};
        this.setOptions($merge(this.presets, options)).build();
        this.bound = {
            window: this.reposition.bind(this, [null]),
            scroll: this.checkTarget.bind(this),
            close: this.close.bind(this),
            key: this.onKey.bind(this)
        };
        this.isOpen = this.isLoading = false;
    },
    assign: function(objs, options) {
        this.initialize(options);
        $each(objs, function(obj){
            this.parse(obj, options);
        }.bind(this));
    },
    extend: function(properties) {
        return $extend(this, properties);
    },
    toggleListeners: function(state) {
        var fn = (state) ? 'addEvent' : 'removeEvent';
        this.closeBtn[fn]('click', this.bound.close);
        this.overlay[fn]('click', this.bound.close);
        document[fn]('keydown', this.bound.key)[fn]('mousewheel', this.bound.scroll);
        window[fn]('resize', this.bound.window)[fn]('scroll', this.bound.window);
    },
    toggleOverlay: function(state, container) {
        this.toggleListeners(state);
        if(state) {
                var pos = {};
            if(container && $(container)) {
                pos = $(container).getCoordinates();
            } else {
                var size = document.getSize(), scroll = document.getScroll();
                pos = { top: scroll.y , left: scroll.x, width: size.x, height: size.y };
            }
            this.overlay.set({ 'styles': { cursor:'pointer', display: 'block', top: pos.top+'px', left: pos.left+'px', width: pos.width+'px', height: pos.height+'px', visibility: 'visible', opacity: this.options.opacity || this.presets.opacity } });
        } else {
            this.overlay.set({ 'styles' : {display: 'none', visibility: 'hidden', opacity: 0} });
        }
    },
    onKey: function(e) {
        switch (e.key) {
            case 'esc': this.close(e);
            case 'up': case 'down': return false;
        }
    },
    checkTarget: function(e) {
        return this.cnt.hasChild(e.target);
    },
    close: function(e) {
        var stoppable = ($type(e) == 'event');
        if (stoppable) e.stop();
        if (!this.isOpen || stoppable) return this;
        this.fx.overlay.start(0).chain(this.toggleOverlay.bind(this));
        this.winWrapper.setStyle('display', 'none');
        this.trash();
        this.toggleListeners();
        this.isOpen = false;
        this.fireEvent('onClose', [this.cnt]);
        return this;
    },
    removeEvents: function(type){
        if (!this.$events) return this;
        if (!type) this.$events = null;
        else if (this.$events[type]) this.$events[type] = null;
        return this;
    },
    trash: function() {
        this.options = {};
        this.element = null;
        this.holderCnt  = null;
        this.holder.empty();
        this.removeEvents().setOptions(this.presets).callChain();
    },
    hideContent: function() {
        if (!this.cnt.get('opacity')) this.fireEvent('onHide', [this.cnt]);
        this.fx.cnt.set(0);
    },
    showContent: function() {
        if (this.cnt.get('opacity')) this.fireEvent('onShow', [this.cnt]);
        var contentSize = window.retrieve('modalcontentsize');
        var headSize = window.retrieve('modalheadsize');
        this.cnt.set('styles', { display: '', width: contentSize.x+'px', height: contentSize.y+'px'});
        this.head.set('styles', { display: '', width: headSize.x+'px', height: headSize.y+'px'});
        this.fx.head.start(1);
        this.fx.cnt.start(1);
        this.callChain();
    },
    toggleLoading: function(state, loadCntr) {
        if(state) {
            if(loadCntr == 'overlay' && !this.isOpen) {
                this.container.addClass('load-l');
            } else if(loadCntr == 'overlay'){
                this.container.addClass('load-s');
            } else {
                this.cntWrapper.addClass('load-large');
            }
            this.overlay.set('styles', { cursor: 'wait'});
            this.fireEvent('onLoading', [this.win]);
        } else {
            this.overlay.set('styles', { cursor: 'pointer'});
            if(this.container.hasClass('load-l')) this.container.removeClass('load-l');
            if(this.container.hasClass('load-s')) this.container.removeClass('load-s');
            if(this.cntWrapper.hasClass('load-large')) this.cntWrapper.removeClass('load-large');
        }
        this.isLoading = state;
    },
    reposition: function() {
        var overlayPos = window.retrieve('modaloverlay') || 'document';
        var size = document.getSize(), scroll = document.getScroll();
        if(overlayPos == 'document') {
            this.overlay.setStyles({ left: scroll.x + 'px', top: scroll.y + 'px', width: size.x + 'px', height: size.y + 'px' });
        }
        this.winWrapper.setStyles({
            left: (scroll.x + (size.x - this.winWrapper.offsetWidth + 6) / 2).toInt() + 'px',
            top: (scroll.y + (size.y - this.winWrapper.offsetHeight + 10 ) / 3).toInt() + 'px'
        });

        return this.fireEvent('onMove', [this.overlay, this.winWrapper]);
    },
    build: function() {
        this.container = new Element('div', { id: this.options.cssName});
        this.overlay = new Element('div', { id: this.options.cssName+'-overlay'});
        this.overlay.set({ 'styles' : { display: 'none', zIndex: this.options.idx, position: 'absolute', opacity: this.options.opacity } });
        this.cnt = new Element('div', { id: this.options.cssName+'-cnt'});
        this.cntPad = new Element( "div", { id: this.options.cssName+'-cnt-pad'}).adopt(this.cnt);
        this.cntWrapper = new Element('div', { id: this.options.cssName+'-cnt-wrapper', 'class': 'pkg'}).adopt(this.cntPad);
        this.closeBtn = new Element('a', { id: this.options.cssName+'-close', 'html': 'Đóng'});
        this.closeBtn.addEvent('click', function(event){ event.stopPropagation(); this.close(); }.bind(this));
        this.head = new Element('div', { id: this.options.cssName+'-head'});
        this.headWrapper = new Element('div', { id: this.options.cssName+'-head-wrapper', 'class': 'pkg'}).adopt(this.head, this.closeBtn);
        this.win = new Element('div', { id: this.options.cssName+'-win', 'class': 'pkg'}).adopt(this.headWrapper, this.cntWrapper);
        this.winWrapper = new Element('div', { id: this.options.cssName+'-win-wrapper'});
        this.winWrapper.set({ 'styles' : { display: 'none', zIndex: this.options.idx+2, position: 'absolute'} }).adopt(new Element('div', { id: this.options.cssName+'-win-pad'}).adopt(this.win));
        this.holder = new Element('div', { id: this.options.cssName+'-holder'});
        this.holder.set({ 'styles':{ display: 'block', zIndex: -1, position: 'absolute', opacity: '0', visibility: 'hidden', width: window.getSize().x } });
        this.container.adopt(this.overlay, this.winWrapper, this.holder);
        this.fx = {
            overlay : new Fx.Tween(this.overlay, $merge({ property: 'opacity', onStart: Events.prototype.clearChain, duration: this.options.duration/4, link: 'cancel' }, this.options.overlayFx)).set(0),
            cnt : new Fx.Tween(this.cnt, $merge({ property: 'opacity', duration: this.options.duration, link: 'chain' }, this.options.contentFx)).set(0),
            head: new Fx.Tween(this.head, $merge({ property: 'opacity', duration: this.options.duration, link: 'chain' }, this.options.headFx)).set(0),
            winWrapper : new Fx.Tween(this.winWrapper, $merge({ property: 'opacity', duration: this.options.duration, link: 'cancel' }, this.options.winWrapperFx)).set(0)
        }
        this.overlay.addEvent('click', function(event){ event.stopPropagation(); this.close(); }.bind(this));
        $(document.body).adopt(this.container);
    },
    parse: function(obj, options) {
        if(obj.retrieve('modalParsed')) return;
        obj.store('modalParsed', true);
        if(!this.container) this.initialize(options);
        var type = $pick(options.type, this.presets.type);
        if(type == '') {
            if((/\.(?:jpg|png|gif|bmp)$/i).test(obj.get('href'))) {
                type = 'image';
            } else if(this.options.ajaxMarker && obj.get(this.options.ajaxMarker)) {
                type = 'ajax';
            } else if(this.options.cloneMarker && obj.get(this.options.cloneMarker)) {
                type = 'clone';
            } else {    type = 'iframe'; }
        }
        var presets = { 'type': type};
        obj.store('modalpreset', $merge(options, presets));
        obj.addEvent('click', function(event){
            event.stop();
            this.element = obj;
            this.onLinkClick(obj);
            return false;
        }.bind(this));
    },
    onLinkClick: function(obj) {
        this.options = obj.retrieve('modalpreset');
        var opt = $merge(this.presets, this.options);
        if(!this.handler[opt.type]) return this.onError(opt.type+' is not supported yet.');
        if(opt.overlay !='none') {
            if(opt.overlay == 'document') {
                this.toggleOverlay(true);
            } else if(opt.overlay == 'caller') {
                this.toggleOverlay(true, obj.get('id'));
            } else { this.toggleOverlay(true, opt.overlay ); }
        }
        window.store('modaloverlay', opt.overlay);
        this.toggleLoading(true, opt.loadCntr || this.presets.loadCntr);
        this.handler[opt.type].generate(obj, opt);
    },
    applyContent: function(content, contentSize, headSize, headPos) {
        if(!content || content == '') return;
        this.cnt.empty();
        this.cnt.set('styles', { display:'none'});
        this.head.set('styles', { display:'none', width: '1px', height: '1px'});
        this.fx.head.set(0);
        this.hideContent();
        if (['string', 'array', false].contains($type(content))) this.cnt.set('html', content || '');
        else this.cnt.adopt(content);
        if (!this.isOpen) {
            this.resize(contentSize, headSize, headPos, true);
            this.isOpen = true;
            this.fireEvent('onOpen', [this.cnt]);
        } else {
            this.resize(contentSize, headSize, headPos, false);
        }
    },
    resize: function(contentSize, headSize, headPos, instantly) {
        window.store('modalcontentsize', contentSize);
        window.store('modalheadsize', headSize);
        var box = document.getSize(), scroll = document.getScroll();
        var cntPadTo = { width: contentSize.x+'px', height: contentSize.y+'px', opacity: 1};
        var temp = { width: contentSize.x, height: contentSize.y};
        var temp2 = { width: (this.presets.cntPad[1]+this.presets.cntPad[3]) , height: (this.presets.cntPad[0]+this.presets.cntPad[2])};
        if(headPos == 'top') {
            temp.width += temp2.width;
            temp.height += (temp2.height+headSize.y+this.presets.headPad[0]+this.presets.headPad[2]);
            this.cntWrapper.set('styles',{ display: '', 'float': ''});
            this.headWrapper.set('styles',{ display: '', 'float': ''});
        } else if(headPos == 'bottom') {
            //not supported yet
            this.cntWrapper.set('styles',{ display: '', 'float': ''});
            this.headWrapper.set('styles',{ display: '', 'float': ''});
        } else if(headPos == 'right') {
            temp.width += (temp2.width+headSize.x+this.presets.headPad[1]+this.presets.headPad[3]);
            temp.height += temp2.height;
            this.cntWrapper.set('styles',{ display: 'block', 'float': 'left'});
            this.headWrapper.set('styles',{ display: 'block', 'float': 'right'});
            this.head.set('styles',{ 'float': 'left'});
            this.closeBtn.set('styles',{ 'float': 'right'});
        } else if(headPos == 'left') {
            temp.width += (temp2.width+headSize.x+this.presets.headPad[1]+this.presets.headPad[3]);
            temp.height += temp2.height;
            this.cntWrapper.set('styles',{ display: 'block', 'float': 'right'});
            this.headWrapper.set('styles',{ display: 'block', 'float': 'left'});
            this.head.set('styles',{ 'float': 'right'});
            this.closeBtn.set('styles',{ 'float': 'left'});
        } else {
            temp.width += temp2.width;
            temp.height += temp2.height;
            this.cntWrapper.set('styles',{ display: '', 'float': ''});
            this.headWrapper.set('styles',{ display: 'none', 'float': ''});
        }
        winTo = { height: temp.height+'px', width: temp.width+'px', opacity: 1 };
        temp.width += (this.presets.winPad[1]+this.presets.winPad[3]);
        temp.height += (this.presets.winPad[0]+this.presets.winPad[2]);
        winWTo = { opacity:0, display: '', left: ((scroll.x + (box.x - temp.width + 6) / 2).toInt())+'px', width: temp.width+'px', top: ((scroll.y + (box.y - temp.height + 10) / 3).toInt())+'px', height: temp.height+'px' };
        this.clearChain();
        this.chain(
            function() {
                this.winWrapper.set('styles', winWTo);
                this.win.set('styles', winTo);
                this.cntPad.set('styles', cntPadTo);
                this.callChain();
            },
            function(){
                this.fx.winWrapper.start(1);
                this.callChain();
            },
            this.showContent, this.reposition,
            function(){
                this.clearChain();
            }
        );
        this.toggleLoading(false);
        this.callChain();
    },
    resizeY : function(sizeImg, size) {
        if(sizeImg.y > size.y) {
            sizeImg.x = (size.y * sizeImg.x / sizeImg.y).toInt();
            sizeImg.y = size.y;
        }
        return sizeImg;
    },
    resizeX: function (sizeImg, size) {
        if(sizeImg.x > size.x) {
            sizeImg.y = (size.x * sizeImg.y / sizeImg.x).toInt();
            sizeImg.x = size.x;
        }
        return sizeImg;
    },
    onError: function(msg) {
        var accLeftBox = $pick(window.retrieve('accLeftBox'), new LBP.Alertx({ duration: 3000, position: 'upperLeft', type: 'errorbx'}));
        if(this.isOpen) this.close();
        this.toggleLoading(false);
        accLeftBox.generateAlert(msg);
        this.toggleOverlay(false);
        this.fireEvent('onError');
        window.store('accLeftBox', accLeftBox);
    },
    handler: new Hash({})
});
LBP.Modal.extend(new Events($empty)).extend(new Options($empty)).extend(new Chain($empty));
LBP.Modal.handler.extend({
    ajax: new Hash({
        generate: function(obj, options) {
            var ajax = this;
            var modalUrl = obj.get('href');
            var caller = (options.ajaxMarker && obj.get(options.ajaxMarker))? obj.get(options.ajaxMarker) : 'modal';
            var modalRequest = new Request({
                method: 'post',
                url: modalUrl,
                async: false,
                autoCancel: true,
                onSuccess: function(response){ ajax.onSuccess(response, obj); },
                onFailure: function(instant){ ajax.onFailure(instant); },
                onException: function(header, value){ ajax.onException(header,value); },
                onRequest: function(){ ajax.onRequest(); }
            }).send( 'sendby='+caller+'&dmode=html_ajax' );
        },
        onSuccess: function(responseStr,obj) {
            var content ='', heading = false, injectMode = '';
            try {
            var responseArr = JSON.decode(responseStr);
            } catch(err){
                var temp = new Element("div").adopt(new Element("code", { 'html': "<b>"+err.message+"</b>"+err.stack.clean()+''}));
                content = temp.get('html');
                heading = "Có lỗi \273 "+err.name;
                injectMode = 'overwrite';
            }

            $each( responseArr, function(elementData, elementID, arr){
                if(elementID == 'modal' || elementID == 'modal-content') {
                    content = elementData['html'];
                    injectMode = elementData['inject'];
                } else if(elementID == 'modal-heading') {
                    heading = elementData['html'];
                } else if($(elementID)) {
                    var injectMethod = elementData['inject'];
                    var newHTML = elementData['html'];
                    if(injectMethod == 'overwrite') {
                        $(elementID).set('html', newHTML);
                    } else if( injectMethod=='destroy') {
                        $(elementID).destroy();
                    } else if( injectMethod == 'before') {
                        $(elementID).set('html', newHTML + ($(elementID).get('html')) );
                    } else if( injectMethod == 'after') {
                        $(elementID).set('html', ($(elementID).get('html')) + newHTML );
                    }
                    LBP.processScripts( newHTML );
                }
            });
            this.applyContent(content, injectMode, heading,obj);
            LBP.runProcesses();
        },
        applyContent: function(content, injectM, heading,obj) {
            try {
                //need to add support for injectmode
            var modal = LBP.Modal;
            var contentSize = $pick(modal.options.size, false);
            var headSize = { x:0, y:0 }, headPos = 'none';
            var ijtdContent = null;
            if(!contentSize) {
                modal.holder.empty();
                modal.holder.set('html',content);
                LBP.processScripts(content);
                ijtdContent = modal.holder.getFirst();
                if(ijtdContent) {
                    contentSize = ijtdContent.getSize();
                } else {
                    return;
                }
            }

            var size = window.getSize();
            size.x -= (modal.presets.cntPad[1]+modal.presets.cntPad[3]+modal.presets.winPad[1]+modal.presets.winPad[3]+20);
            size.y -= (modal.presets.cntPad[0]+modal.presets.cntPad[2]+modal.presets.winPad[0]+modal.presets.winPad[2]+20);
            modal.options.header = $pick(modal.options.header, modal.presets.header);
            if(modal.options.txtMarker && modal.options.txtMarker !='ajax') {
                var title = obj.get(modal.options.txtMarker);
                if(title != '') heading = title;
            }
            if(heading || modal.options.header) {
                modal.head.set('html', (heading)? heading :'ACC');
                headSize = (modal.options.headSize)? modal.options.headSize : ( (modal.head && modal.isOpen)? modal.head.getSize() : modal.presets.headSize);
                headPos = $pick(modal.options.headPos, modal.presets.headPos);
                if(headPos == 'top' || headPos == 'bottom'){
                    size.y -= (headSize.y+modal.presets.headPad[0]+modal.presets.headPad[2]);
                    contentSize.x = Math.min(contentSize.x, size.x);
                    contentSize.y = Math.min(contentSize.y, size.y);
                    headSize = { y: (contentSize.y >0)? headSize.y : modal.presets.headSize.y, x: contentSize.x-16};
                } else {
                    size.x -= (headSize.x+modal.presets.headPad[1]+modal.presets.headPad[3]);
                    contentSize.x = Math.min(contentSize.x, size.x);
                    contentSize.y = Math.min(contentSize.y, size.y);
                    headSize = { x: (headSize.x>0)? headSize.x : modal.presets.headSize.x, y: contentSize.y};
                }
            } else {
                contentSize.x = Math.min(contentSize.x, size.x);
                contentSize.y = Math.min(contentSize.y, size.y);
            }
            modal.cnt.empty();
            modal.cnt.set('styles', { display:'none', 'overflow': 'auto'});
            if(ijtdContent != null) {
                modal.cnt.adopt(ijtdContent);
            } else {
                modal.cnt.set('html', content);
                LBP.processScripts( content );
            }
            modal.head.set('styles', { display:'none', width: '1px', height: '1px'});
            modal.fx.head.set(0);
            modal.hideContent();
            if (!modal.isOpen) {
                modal.resize(contentSize, headSize, headPos, true);
                modal.isOpen = true;
                modal.fireEvent('onOpen', [this.cnt]);
            } else {
                modal.resize(contentSize, headSize, headPos, false);
            }
            }
            catch(err) {
                LBP.Modal.onError('Error !!! Check your code T_T ');
            }
        },
        onRequest: function(){},
        onException: function(header, value) {
            LBP.Modal.isOpen = true;
            LBP.Modal.onError(header+' : '+value);
            //LBP.Modal.close();
        },
        onFailure: function(instance){
            LBP.Modal.isOpen = true;
           LBP.Modal.onError(instance.status + ' ' + instance.statusText);
           //LBP.Modal.close();
        }
    }),
    clone: new Hash({
        generate: function(obj, options){
            var modal = LBP.Modal;
            if(!options.cloneMarker ) return modal.onError('Clone Marker ?');
            var cloneId = (options.cloneMarker == 'href')? obj.get(options.cloneMarker).split('#')[1]: obj.get(options.cloneMarker);
            if(cloneId =='' || !$(cloneId)) return modal.onError('Cloned Div error');
            modal.holderCnt = null;
            modal.holderCnt = $(cloneId).clone();
            modal.holderCnt.set('html', $(cloneId).get('html'));
            modal.holder.empty();
            modal.holderCnt.set('styles', { 'display':'block'});
            modal.holder.adopt(modal.holderCnt);
            var sizeImg = modal.holderCnt.getSize();
            var size = window.getSize();
            size.x -= (modal.presets.cntPad[1]+modal.presets.cntPad[3]+modal.presets.winPad[1]+modal.presets.winPad[3]+20);
            size.y -= (modal.presets.cntPad[0]+modal.presets.cntPad[2]+modal.presets.winPad[0]+modal.presets.winPad[2]+20);
            modal.options.header = $pick(modal.options.header, modal.presets.header);
            if(sizeImg.x > size.x) {
                modal.cnt.set('styles',{ overflow: 'auto'});
            } else {
                modal.cnt.set('styles',{ overflow: 'hidden'});
            }
            if(modal.options.header) {
                var title = $pick(obj.get($pick(options.txtMarker, 'title')), 'ACC');
                modal.head.set('html', title);
                var headSize = $pick(modal.options.headSize, modal.head.getSize(), modal.presets.headSize);
                var headPos = $pick(modal.options.headPos, modal.presets.headPos);
                if(headPos == 'top' || headPos == 'bottom'){
                    size.y -= (headSize.y+modal.presets.headPad[0]+modal.presets.headPad[2]);
                    sizeImg.x = Math.min(sizeImg.x, size.x);
                    sizeImg.y = Math.min(sizeImg.y, size.y);
                    headSize = { y: (headSize.y >0)? headSize.y : modal.presets.headSize.y, x: sizeImg.x-16};
                } else {
                    size.x -= (headSize.x+modal.presets.headPad[1]+modal.presets.headPad[3]);
                    sizeImg.x = Math.min(sizeImg.x, size.x);
                    sizeImg.y = Math.min(sizeImg.y, size.y);
                    headSize = { x: (headSize.x>0)? headSize.x : modal.presets.headSize.x, y: sizeImg.y};
                }
            } else {
                sizeImg.x = Math.min(sizeImg.x, size.x);
                sizeImg.y = Math.min(sizeImg.y, size.y);
                var headSize = {x:0, y:0};
                var headPos = 'none';
            }
            modal.applyContent(modal.holderCnt, sizeImg, headSize, headPos);
        }

    }),
    image: new Hash ({
        generate: function(obj, options) {
            LBP.Modal.holderCnt = null;
            var url = obj.get('href') || obj.get('src');
            LBP.Modal.holderCnt = new Asset.image(url, { onload: this.onLoad.bind(this), onerror: this.onError.bind(LBP.Modal), onabort: this.onAbort.bind(LBP.Modal) });
            if(options.header) {
                options.txtMarker = $pick(options.txtMarker, 'caption');
                var title = $pick(obj.get(options.txtMarker), obj.get('title'), 'ACC >> Xem ảnh');
                LBP.Modal.head.set('html', title);
            } else {
                LBP.Modal.options.headPos = 'none';
            }
        },
        onAbort: function() {
            this.onError('Đã ngưng việc tải hình');
            return this.fireEvent('onAbort');
        },
        onError: function() {
            this.onError('Không tìm thấy hình');
        },
        onLoad: function(img) {
            var modal = LBP.Modal;
            var sizeImg = { x: img.width, y: img.height};
            var size = window.getSize();
            size.x -= (modal.presets.cntPad[1]+modal.presets.cntPad[3]+modal.presets.winPad[1]+modal.presets.winPad[3]+20);
            size.y -= (modal.presets.cntPad[0]+modal.presets.cntPad[2]+modal.presets.winPad[0]+modal.presets.winPad[2]+20);
            var sizeHead = { x: 0, y: 0};
            modal.options.header = $pick(modal.options.header, modal.presets.header);
            if(modal.options.header) {
                var headSize = $pick(modal.options.headSize, modal.head.getSize(), modal.presets.headSize);
                var headPos = $pick(modal.options.headPos, modal.presets.headPos);
                if(headPos == 'top' || headPos == 'bottom'){
                    size.y -= (headSize.y+modal.presets.headPad[0]+modal.presets.headPad[2]);
                    sizeImg = modal.resizeY(sizeImg, size);
                    sizeImg = modal.resizeX(sizeImg, size);
                    sizeHead = { y: (headSize.y>0)? headSize.y : modal.presets.headSize.y, x: sizeImg.x-16};
                } else {
                    size.x -= (headSize.x+modal.presets.headPad[1]+modal.presets.headPad[3]);
                    sizeImg = modal.resizeX(sizeImg, size);
                    sizeImg = modal.resizeY(sizeImg, size);
                    sizeHead = { x: (headSize.x>0)? headSize.x : modal.presets.headSize.x, y: sizeImg.y};
                }
            } else {
                sizeImg = modal.resizeX(sizeImg, size);
                sizeImg = modal.resizeY(sizeImg, size);
            }
            modal.holderCnt = $(img);
            var width = img.width;
            var height = img.height;
            modal.holderCnt.set('styles', { width: sizeImg.x+'px', height: sizeImg.y+'px'});
            if(sizeImg.x < img.width || sizeImg.y <img.height) {
                modal.holderCnt.store('show', 'thumb');
                modal.holderCnt.set('styles', { cursor: 'pointer'});
                modal.holderCnt.addEvent( 'click', function(event){
                    event.stopPropagation();
                    if(modal.holderCnt.retrieve('show') == 'thumb') {
                        modal.cnt.set('styles', { overflow: 'auto' });
                        modal.holderCnt.set('styles', { width: width+'px', height: height+'px'});
                        modal.holderCnt.store('show', 'img');
                    } else {
                        modal.cnt.set('styles', { overflow: 'hidden' });
                        modal.holderCnt.set('styles', { width: sizeImg.x+'px', height: sizeImg.y+'px'});
                        modal.holderCnt.store('show', 'thumb');
                    }
                });
            }
            modal.applyContent(modal.holderCnt, sizeImg, sizeHead, headPos);
        }
    }),
    inline: new Hash({
        generate: function(content, heading, options) {
        	var modal = LBP.Modal;
            if(!modal.winWrapper) modal.initialize(options);
            options = options || {};
            options.overlay = $pick(options.overlay, modal.presets.overlay);
            if(options.overlay !='none') {
                if(options.overlay == 'document') {
                    modal.toggleOverlay(true);
                } else { this.toggleOverlay(true, options.overlay ); }
            }
            var contentSize = $pick(options.size, false);
            var headSize = { x:0, y:0 }, headPos = 'none';
            var ijtdContent = null;
            if(!contentSize) {
                modal.holder.empty();
                modal.holder.set('html',content);
                ijtdContent = modal.holder.getFirst();
                if($type(content) == 'string') LBP.processScripts( content );
                contentSize = ijtdContent.getSize();
            }
            var size = window.getSize();
            size.x -= (modal.presets.cntPad[1]+modal.presets.cntPad[3]+modal.presets.winPad[1]+modal.presets.winPad[3]+20);
            size.y -= (modal.presets.cntPad[0]+modal.presets.cntPad[2]+modal.presets.winPad[0]+modal.presets.winPad[2]+20);
            options.header = $pick(options.header, modal.presets.header);

            if(heading || options.header) {
                modal.head.set('html', (heading)? heading :'ACC');
                headSize = (modal.options.headSize)? modal.options.headSize : ( (modal.head && modal.isOpen)? modal.head.getSize() : modal.presets.headSize);
                headPos = $pick(modal.options.headPos, modal.presets.headPos);
                if(headPos == 'top' || headPos == 'bottom'){
                    size.y -= (headSize.y+modal.presets.headPad[0]+modal.presets.headPad[2]);
                    contentSize.x = Math.min(contentSize.x, size.x);
                    contentSize.y = Math.min(contentSize.y, size.y);
                    headSize = { y: (contentSize.y >0)? headSize.y : modal.presets.headSize.y, x: contentSize.x-16};
                } else {
                    size.x -= (headSize.x+modal.presets.headPad[1]+modal.presets.headPad[3]);
                    contentSize.x = Math.min(contentSize.x, size.x);
                    contentSize.y = Math.min(contentSize.y, size.y);
                    headSize = { x: (headSize.x>0)? headSize.x : modal.presets.headSize.x, y: contentSize.y};
                }
            } else {
                contentSize.x = Math.min(contentSize.x, size.x);
                contentSize.y = Math.min(contentSize.y, size.y);
            }
            modal.cnt.empty();
            modal.cnt.set('styles', { display:'none', 'overflow': 'auto'});
            modal.toggleListeners(true);
            if(ijtdContent != null) {
                modal.applyContent(ijtdContent, contentSize, headSize, headPos);
            } else {
                modal.applyContent(content, contentSize, headSize, headPos);
                if($type(content) == 'string') LBP.processScripts( content );
            }
        }
    }),
    confirm: new Hash({
        presets: {
            content: 'Bạn chắc chắn muốn xóa?',
            msgMarker: 'confirm',
            css: 'button',
            wrapper: 'button-wrapper',
            callerMarker: 'rev',
            button:[
                {
                    id: 'button_ok',
                    action: 'selfajax', //selfajax, ajax, cancel, ...
                    caller: '',
                    label: 'OK',
                    fn: $empty
                }, {
                    id: 'button_cancel',
                    action: 'cancel',
                    label: 'Cancel',
                    fn: $empty }]
        },
        generate: function(obj, options) {
            this.options = $merge(this.presets, options || {} );
            var msg = this.options.content;
            var content = new Element("div", { "class": 'modal-confirm-wrapper'});

            this.buttons = new Element("div", { 'class': 'modal-buttons pkg' });
            this.options.button.each(function(btn){
                if(this[btn.action]) {
                    var temp = this[btn.action](obj, btn.id, btn.caller);
                    temp.adopt(new Element("div",{ 'class': this.options.css, 'html': btn.label }));
                    temp.addEvents({
                        'mouseenter': function(event) {
                            if(!temp.hasClass('modal-btn-hover')) temp.addClass('modal-btn-hover');
                            event.stopPropagation();
                        }.bind(this),
                        'mouseleave': function(event) {
                            if(temp.hasClass('modal-btn-hover')) temp.removeClass('modal-btn-hover');
                            event.stopPropagation();
                        }.bind(this),
                        'click': function(event) {
                            this[btn.action+'Click'](event, temp, btn);
                            if(btn.fn) {
                                btn.fn.run([obj,temp, btn]);
                            }
                        }.bind(this)
                    });
                    this.buttons.adopt(temp);
                }
            }.bind(this));

            if($type(msg)=='element') {
                content.adopt(msg, this.buttons);
                if(!msg.hasClass('modal-confirm-msg')){ msg.addClass('modal-confirm-msg'); }
            } else {
                if(msg == 'element') {
                    msg = obj.get(this.options.msgMarker);
                    if(msg) {
                        obj.removeProperty(this.options.msgMarker);
                        obj.store('modalmsg', msg);
                    } else {
                        msg = obj.retrieve('modalmsg');
                    }
                }
                content.adopt( new Element("div", { "class": "modal-confirm-msg", 'html': msg }), this.buttons);
            }

            var modal = LBP.Modal;
            if(!modal.winWrapper) modal.initialize(options);
            //options = this.options;
            options.overlay = $pick(options.overlay, modal.presets.overlay);
            if(options.overlay !='none') {
                if(options.overlay == 'document') {
                    modal.toggleOverlay(true);
                } else { this.toggleOverlay(true, options.overlay ); }
            }
            var headSize = { x:0, y:0 }, headPos = 'none';
            var ijtdContent = null;
            modal.holder.empty();
            modal.holder.adopt(content);
            ijtdContent = content;
            LBP.processScripts( content.get('html') );
            contentSize = ijtdContent.getSize();
            var size = window.getSize();
            size.x -= (modal.presets.cntPad[1]+modal.presets.cntPad[3]+modal.presets.winPad[1]+modal.presets.winPad[3]+20);
            size.y -= (modal.presets.cntPad[0]+modal.presets.cntPad[2]+modal.presets.winPad[0]+modal.presets.winPad[2]+20);
            options.header = $pick(options.header, modal.presets.header);

                modal.head.set('html', (options.header)? options.header :'ACC \273 Confirm');
                headSize = (modal.options.headSize)? modal.options.headSize : ( (modal.head && modal.isOpen)? modal.head.getSize() : modal.presets.headSize);
                headPos = $pick(modal.options.headPos, modal.presets.headPos);
                if(headPos == 'top' || headPos == 'bottom'){
                    size.y -= (headSize.y+modal.presets.headPad[0]+modal.presets.headPad[2]);
                    contentSize.x = Math.min(contentSize.x, size.x);
                    contentSize.y = Math.min(contentSize.y, size.y);
                    headSize = { y: (contentSize.y >0)? headSize.y : modal.presets.headSize.y, x: contentSize.x-16};
                } else {
                    size.x -= (headSize.x+modal.presets.headPad[1]+modal.presets.headPad[3]);
                    contentSize.x = Math.min(contentSize.x, size.x);
                    contentSize.y = Math.min(contentSize.y, size.y);
                    headSize = { x: (headSize.x>0)? headSize.x : modal.presets.headSize.x, y: contentSize.y};
                }
            modal.cnt.empty();
            modal.cnt.set('styles', { display:'none', 'overflow': 'auto'});
            modal.toggleListeners(true);
            if(ijtdContent != null) {
                modal.applyContent(ijtdContent, contentSize, headSize, headPos);
            } else {
                modal.applyContent(content, contentSize, headSize, headPos);
                if($type(content) == 'string') LBP.processScripts( content );
            }
        },
        customClick: function(event) { event.stopPropagation(); },
        custom: function(obj, id, caller) {
            var temp = new Element("a", {
                "id" : id,
                "class" : this.options.wrapper,
                "href" : "javascript:void(0);"
            });
            return temp;
        },
        selfajaxClick: function(event, obj, btn) {
            event.stop();
            var modalUrl = obj.get('href');
            var caller = obj.get('caller') || 'modal';
            var ajax = this;
            var modalRequest = new Request({
                method: 'post',
                url: modalUrl,
                async: false,
                autoCancel: true,
                onSuccess: function(response){ LBP.Modal.handler.ajax.onSuccess(response, obj); },
                onFailure: function(instant){ LBP.Modal.handler.ajax.onFailure(instant); },
                onException: function(header, value){ LBP.Modal.handler.ajax.onException(header,value); }
            }).send( 'sendby='+caller+'&dmode=html_ajax' );
        },
        selfajax: function(obj, id, caller) {
            var temp = new Element("a", {
                "id" : id,
                "class" : this.options.wrapper,
                "href" : obj.get('href'),
                "caller" : $pick(obj.get(this.options.callerMarker), obj.retrieve('modalcaller'))
            });
            if(obj.get(this.options.callerMarker)) {
                obj.store('modalcaller', obj.get(this.options.callerMarker));
                obj.removeProperty(this.options.callerMarker);
            }

            return temp;
        },
        cancelClick: function() {
            LBP.Modal.close();
        },
        cancel: function(obj, id, caller) {
            var temp = new Element("a", {
                "id" : id,
                "class" : this.options.wrapper,
                "href" : "javascript:void(0);"
            });
            return temp;
        }
    })
});

LBP.RadioButtons = new Class({
    Implements: [Options, Events],
	options: {
		wrapperName: 'radio-button', // class
		divClick: null,
		checkboxClick: null,
		divOver: null,
		divOut: null
	},

	initialize: function(elements, options){
		this.setOptions(options);
		elements.each(function(theDiv){
            if (!theDiv.retrieve('checkboxInstalled')){
				theDiv.store('checkboxInstalled', true);

				theDiv.addEvent('click', function(){
					this.onDivClick(theDiv);
				}.bind(this));

				theDiv.addEvent('mouseover', function(){
					this.onDivOver(theDiv);
				}.bind(this));

                theDiv.addEvent('mouseout', function(){
					this.onDivOut(theDiv);
				}.bind(this));

				var radioButton = theDiv.getElement('input[type=radio]');

				radioButton.addEvent('click', function(){
					this.onButtonClick(radioButton, theDiv);
				}.bind(this));

				if (radioButton.checked)
					theDiv.addClass(this.options.wrapperName + '-checked');
			}
		}.bind(this));
	},

	onDivClick: function(theDiv){
        var radioButton = theDiv.getElement('input[type=radio]');
        if (!radioButton.checked){
			radioButton.checked = !radioButton.checked;
		}

		radioButton.fireEvent('click');
		(this.options.divClick || this.divClick).call(this, theDiv);
	},

	onButtonClick: function(radioButton, theDiv){
        if (radioButton.checked) {
        	$$('input[name=' + radioButton.name + ']').each(function(currentButton){
        		buttonDiv = currentButton.getParent('.' + this.options.wrapperName);
        		buttonDiv.removeClass(this.options.wrapperName + '-checked');
			}.bind(this));
			theDiv.addClass(this.options.wrapperName + '-checked');
		}
		/*else {
        	$$('input[name=viewperm]').each(function(currentButton){
        		buttonDiv = currentButton.getParent('.' + this.options.wrapperName);
        		buttonDiv.addClass(this.options.wrapperName + '-checked');
			}.bind(this));
			theDiv.removeClass(this.options.wrapperName + '-checked');
		}*/
		(this.options.buttonClick || this.buttonClick).call(this, radioButton, theDiv);
	},

	onDivOut: function(theDiv){
        theDiv.removeClass(this.options.wrapperName + '-checked-hover');
		theDiv.removeClass(this.options.wrapperName + '-hover');
		(this.options.divOut || this.divOut).call(this, theDiv);
	},

	onDivOver: function(theDiv){
        if (theDiv.hasClass(this.options.wrapperName + '-checked')){
			theDiv.addClass(this.options.wrapperName + '-checked-hover');
		}
		else {
			theDiv.addClass(this.options.wrapperName + '-hover');
		}
		(this.options.divOver || this.divOver).call(this, theDiv);
	},

	divClick: function(){},
	divOver: function(){},
	divOut: function(){},
	buttonClick: function(){}

});

/**
* Update: 10/29/2008 -- update tip section, will depend on LBP.Tip
*/
LBP.Rating = new Class({
    Implements: [Events, Options],
    options: {
        active: false,  // true to enable voting
        scale: 5,    //the maximal point
        cssName: 'star',
        //style config
        width: 19,
        height: 18,
        fill: 13,
        left: 3,
        right: 3,

        tipWidth: 100,
        tipHeight: 13,

        ext: '.htmx',
        urlMarker: 'url',
        ajaxMarker: 'ajax',

        caption: ['Dở tệ >.<','Thường thôi','Tạm ổn','Hay đấy chứ!','Tuyệt vời!!!'],

        errorMsg: '',       //the msg to fire on window
        onError: $empty,     //fire when user clicked on an inactive rating element
        onRate: $empty  //fire when user vote on an item
    },
    initialize: function(options) {
        this.setOptions(options);
    },
    assign: function(obj,options){
        if(!obj || obj.retrieve('wasAssignedRating')) return;
        obj.store('wasAssignedRating',true);
        this.setOptions(options);
        this.setUp(obj);
    },
    setUp: function(obj) {
        obj.addClass('pkg');
        obj.set('styles',{ height: this.options.height+'px', width: this.options.width*this.options.scale+'px', overflow: 'hidden'});
        var avg = obj.get('avg');
        avg= (avg==null)?0:avg.toFloat();
        var act = avg.toInt();
        var iAct = this.options.scale - act;
        var rem = Math.round( (avg - act)*100)/100;
        this.star = [];
        if(rem > 0) {
            iAct--;
            this.createStar(act,this.options.cssName+'-fill',obj);
            this.createPartial(rem, obj);
        } else if(rem < 0) {
            act--;
            this.createStar(act,this.options.cssName+'-fill',obj);
            this.createPartial(rem, obj);

        } else {
            this.createStar(act,this.options.cssName+'-fill',obj);
        }
        this.createStar(iAct,this.options.cssName+'-empty',obj);
        obj.removeProperty('avg');
        if(this.options.active){
            obj.store('starArr', this.star);
            var temp = obj.get(this.options.urlMarker);
            obj.store('url', temp);
            obj.removeProperty(this.options.urlMarker);
            temp = $pick(obj.get(this.options.ajaxMarker), 'rating');
            obj.store('sendby', temp);
            obj.removeProperty(this.options.ajaxMarker);
            temp = obj.get('captarget');
            if(temp || this.options.caption) {
                obj.removeProperty('captarget');
                var tipCntr = obj.getNext('div.'+temp);
                if(tipCntr) {
                    var dfltTxt = tipCntr.get('html');
                    tipCntr.store('dfltText', dfltTxt);
                } else {
                    tipCntr = new Element("div", { 'class': 'rating-tip'});
                    tipCntr.set('styles', { display: 'none', position: 'absolute', zIndex: 1000, width: this.options.tipWidth+'px', height: this.options.tipHeight+'px'});
                    obj.adopt(tipCntr);
                }
                obj.store('starTip', tipCntr);
            }
            var idxstar= 0;
            this.star.each(function(item){
                item.title=this.options.caption[idxstar] ;
                idxstar++;
                item.addEvents({
                    'mouseenter': function(event){
                        this.onHover(obj, item);
                    }.bind(this),
                    'mouseleave': function(event){
                        this.onOut(obj,item);
                    }.bind(this),
                    'click': function(event){
                        this.onRate(obj,item, this.options.onRate);
                        event.stopPropagation();
                    }.bind(this)
                });
            }.bind(this));
            obj.addEvents({
                'mouseenter': function(event){
                    this.onEnter(obj);
                }.bind(this),
                'mouseleave': function(event){
                    this.onLeave(obj);
                }.bind(this)
            });
           LBP.Tip.assign(this.star, {position: 'vCenter', tipCss: 'tip-content', margin: 0, opacity: 0.9});
        }

    },
    onHover: function(obj, item){
        var stars = obj.retrieve('starArr');
        var idx = stars.indexOf(item);
        for(i=idx; i>=0;i--){
            if(stars[i].hasClass(this.options.cssName+'-hover')) {
                break;
            } else {
                stars[i].addClass(this.options.cssName+'-hover');
            }
        }
        item.addClass(this.options.cssName+'-hover');
    },
    onOut: function(obj,item){
        if(item.hasClass(this.options.cssName+'-hover')) item.removeClass(this.options.cssName+'-hover');
    },
    onEnter: function(obj,item){
        obj.addClass(this.options.cssName+'-focus');
    },
    onLeave: function(obj,item){
        var stars = obj.retrieve('starArr');
        for(i=0; i<stars.length;i++){
            if(stars[i].hasClass(this.options.cssName+'-hover')) stars[i].removeClass(this.options.cssName+'-hover');
        }
        obj.removeClass(this.options.cssName+'-focus');
    },
    createPartial: function(num, obj){
        var style = { 'display': 'block', 'float': 'left', 'overflow': 'hidden', 'height': this.options.height+'px'};
        var fill = new Element("div", { 'class': this.options.cssName+'-fill'});
        var empty = new Element("div", { 'class': this.options.cssName+'-empty'});
        var wrapper = new Element("div", { 'class': this.options.cssName+'-swrap'});

        var width = Math.round(num*this.options.fill);
        fill.set('styles', $merge(style,{
            'width': width+this.options.left+'px'
        }));
        empty.set('styles', $merge(style,{
            'width': this.options.fill-width+this.options.right+'px'
        }));
        wrapper.set('styles', $merge(style,{ 'width': this.options.width+'px'}));
        wrapper.adopt(fill, empty);
        obj.adopt(wrapper);
        this.star.push(wrapper);
    },
    createStar: function(num,css, obj){
        for(i=0;i<num;i++){
            var temp = new Element("div", { 'class': css});
            temp.set('styles',{
                'display': 'block',
                'float': 'left',
                'width': this.options.width+'px',
                'height': this.options.height+'px',
                'overflow': 'hidden'
                });
            obj.adopt(temp);
            this.star.push(temp);
        }
    },
    onRate: function(obj, item, fn){
        var stars = obj.retrieve('starArr');
        var value = stars.indexOf(item)+1;
																        /*'Edit o cho nay 1 ti nha,' => dua nao vay? */
        var url = obj.retrieve('url')+value+this.options.ext;
        var sendby = obj.retrieve('sendby');
        var tempRequest = new Request.LBP({
            'url':    url,
            'caller': sendby,
            onRequest: function(){ LBP.toggleLoading(true, this); }
        }).send('sendby=' + sendby + '&dmode=html_ajax');
        if(fn) fn.run(item,value);
    },
    onError: function(msg, fn){
        window.fireEvent('onError', msg);
        if(fn) fn.run();
    }
});

/**
*   Dependency: LBP.Dropdownlist, tip
*   Updated: tested
*/
LBP.ScrollSlider = new Class({
    Implements: [Events, Options],
    options: {
        total: 0,
        perSlide: 3,
        startPage: 2,
        mode: 'vertical',

        prefix: 'pl-',
        wrapper: '',
        dump: '',
        container: ''
    },
    initialize: function(container, wrapper, options){
        if(!$(container) || !$(wrapper)) return;
        this.container = $(container);
        this.wrapper = $(wrapper);
        this.wrapper.empty();
        this.dump = $(this.options.dump);
        this.setOptions(options);
        this.build();
        this.attach();
        this.container.store('hasSlider', true);
        this.current = 1;
        this.slideTo(this.options.startPage);
        this.container.addEvent('onReload', this.trash.bind(this));
    },
    trash: function() {
        //destroy list
        var selectList = this.dropdownSel.retrieve('selectList');
        selectList.dispose();
        var selectBox = this.dropdownSel.retrieve('selectBox');
        selectBox.dispose();
        this.dump.empty();
        this.container.store('hasSlider', false);
        this.container.removeEvent('onReload', this.trash.bind(this));
    },
    attach: function() {
        this.select = new LBP.Dropdownlists({  wrapperName: 'pl-dropsel', selectListWidthDiff: 0, injectTo: 'wrapper', itemClick: this.clickHandle.bind(this), reverseEgr: true});
        this.prevBtn.addEvents({
            'mouseenter': function(event){
                event.stopPropagation();
                this.onHover(this.prevBtn);
            }.bind(this),
            'mouseleave': function(event){
                event.stopPropagation();
                this.onOut(this.prevBtn);
            }.bind(this),
            'click': function(event){
                event.stopPropagation();
                this.slideTo(this.prev);
            }.bind(this)
        });
        this.nextBtn.addEvents({
            'mouseenter': function(event){
                event.stopPropagation();
                this.onHover(this.nextBtn);
            }.bind(this),
            'mouseleave': function(event){
                event.stopPropagation();
                this.onOut(this.nextBtn);
            }.bind(this),
            'click': function(event){
                event.stopPropagation();
                this.slideTo(this.next);
            }.bind(this)
        });
    },
    slideTo: function(page) {
        if(this.current == page) return;
        var num = (page-1)*this.options.perSlide + 1;
        if(num > this.options.total) return;
        if($(this.options.wrapper+'-entry-'+num)) {
            this.currentEl = $(this.options.wrapper+'-entry-'+num);
            this.scrollFx.toElement(this.currentEl);
            this.current = page;
            this.calculate();
            this.updateButton();
            this.dropdownSel.fireEvent('onReverse',[this.dropdownSel, page]);
        }
    },
    updateButton: function() {
        ['prev','next'].each(function(btn){
            if(this[btn] == 0) {
                if(!this[btn+'Btn'].hasClass('inactive')) this[btn+'Btn'].addClass('inactive')
            } else {
                if(this[btn+'Btn'].hasClass('inactive')) this[btn+'Btn'].removeClass('inactive')
            }
            this[btn+'Btn'].set('html', this[btn]+'');
        }.bind(this));
    },
    onHover: function(item) {
        item.addClass('hover');
    },
    onOut: function(item) {
        if(item.hasClass('hover')) item.removeClass('hover');
    },
    clickHandle:function(selectItem, selectBox, selectList) {
        this.slideTo(selectItem.get('id').toInt());
    },
    calculate: function() {
        this.prev = this.current - 1;
        this.next = (this.current != this.total)? (this.current+1) : 0;
    },
    build: function() {
        this.scrollFx = new Fx.Scroll(this.container, { wheelStops: false});
        this.options.total = this.options.total.toInt();

        this.options.startPage = this.options.startPage.toInt();
        this.options.perSlide = this.options.perSlide.toInt();
        this.total = (this.options.total / this.options.perSlide).toInt();
        if((this.total * this.options.perSlide) < this.options.total) {
            this.total = this.total+1;
        }
        this.current = (this.options.startPage < this.total)? this.options.startPage: this.total;
        this.calculate();
        this.prevBtn = new Element("a",{ 'class': this.options.prefix+'prev', 'href': 'javascript:void(0);', 'html': this.prev+''});
        this.nextBtn = new Element("a",{ 'class': this.options.prefix+'next', 'href': 'javascript:void(0);', 'html': this.next+''});
        this.plText = new Element("span",{ 'class': this.options.prefix+'text pkg '+this.options.prefix+ 'jump-drop'});
        this.dropdownSel = new Element("select");
        for(var i=1;i< this.current; i++) {
            this.dropdownSel.adopt(new Element("option", { 'value': i, 'html': i+''}));
        }
        this.dropdownSel.adopt(new Element("option", { 'value': this.current, 'html': this.current+'', selected:'selected'}));
        for(var i=this.current+1;i<= this.total; i++) {
            this.dropdownSel.adopt(new Element("option", { 'value': i, 'html': i+''}));
        }
        this.plText.adopt(new Element('div',{ 'class': this.options.prefix+'dropsel', 'toWidth': 18 }).adopt(this.dropdownSel));
        this.wrapper.adopt(this.prevBtn, this.plText, this.nextBtn);
        this.updateButton();
    }
});

LBP.STab = new Class({
	Implements: [Options, Events],
	options: {
		currentClassName: 'tab-header-current',
		hoverClassName: 'tab-header-hover'
	},

	initialize: function(tabHeaders, tabContents){
		this.tabHeaders = tabHeaders;
		this.tabContents = tabContents;
		this.setup();
	},

	setup: function(){
		this.tabHeaders.each(function(thisHeader){
			thisHeader.addEvent('click', function(){
				this.swap(this.tabHeaders.indexOf(thisHeader));
			}.bind(this));

			thisHeader.addEvent('mouseover', function(){
				thisHeader.addClass(this.options.hoverClassName);
			}.bind(this));

            thisHeader.addEvent('mouseleave', function(){
            	thisHeader.removeClass(this.options.hoverClassName);
			}.bind(this));
		}.bind(this));

        this.tabContents.each(function(thisContent, idx){
			this.hideSection(idx);
		}.bind(this));

		this.showSection(0);
	},

	swap: function(swapIdx){
		this.tabContents.each(function(thisContent, idx){
			if (swapIdx == idx) {
				this.showSection(idx);
			}
			else {
				this.hideSection(idx);
			}
		}.bind(this));
	},

	hideSection: function(idx){
		this.tabHeaders[idx].removeClass(this.options.currentClassName);
		this.tabContents[idx].setStyle('display', 'none');
	},

	showSection: function(idx){
		this.tabHeaders[idx].addClass(this.options.currentClassName);
		this.tabContents[idx].setStyle('display', 'block');
	}
});

LBP.Tab = new Class({
    Implements: [Events, Options],
    options : {
        mode: 'static', // static || dynamic || auto
        headerCss: '',
        contentCss: '',
        contentInner: '',
        overlayCss: '',
        currentCss:'',
        markers: { url: 'href', caller: ''},
        dfltIdx: -1,
        collapse: '',
        onHover: $empty,
        onOut: $empty,
        onShow: $empty,
        onHide: $empty
    },
    initialize: function(container, options) {
        if(!container || $type(container) != 'element') return;
        this.container = $(container);
        this.setOptions(options);
        this.build();
        if(this.current){ this.current.fireEvent('click');};
        var onreload = this.build.bind(this);
        this.container.addEvent('onReload', onreload);
    },
    build: function() {
        this.items = [];
        this.container.getElements('.'+this.options.contentCss).each(function(content){
            if(!content.retrieve('tabProcessed')) {
                content.set('styles',{ display: 'none'});
                if(this.options.mode =='auto'){
                    var cntHtml = content.get('html');
                    cntHtml = cntHtml.trim();
                    content.store('hasContent', (cntHtml != ''));
                }
                this.items.push(content);
            }
            content.store('tabProcessed', true);
        }.bind(this));
        var i = 0;
        this.container.getElements('.'+this.options.headerCss).each(function(item){
            //try{
            if(!item.retrieve('tabProcessed')) {
                item.store('tabContent', this.items[i]);
                if(this.options.dfltIdx == i+1 || item.hasClass(this.options.currentCss)){ this.current = item; }
                i++;
                item.addEvents({
                    'click': function(event){
                        if(event) { event.stop();}
                        this[this.options.mode+'Click'](item, item.retrieve('tabContent'));
                    }.bind(this),
                    'mouseenter': function(event){ event.stop(); var myfn = this.options.onHover || this.onHover; myfn.run([item, this]); }.bind(this),
                    'mouseleave': function(event){ event.stop(); var myfn = this.options.onOut || this.onOut; myfn.run([item, this]); }.bind(this),
                    'onHide': function(item, tab){ if(this.options.onHide) this.options.onHide.run([item, tab]); }.bind(this),
                    'onShow': function(item, tab){ if(this.options.onShow) this.options.onShow.run([item, tab]); }.bind(this)
                });
                item.store('tabProcessed', true);
                if(this.options.mode != 'static'){
                    $each(this.options.markers, function(mrk, key){
                        var temp = item.get(mrk) || '';
                        if(temp && temp !='') {
                            item.store(key, temp);
                            item.removeProperty(key);
                        };
                    }.bind(this));
                }
            }
            //} catch(err){ window.fireEvent('onError','tab error'); };
            this.session = LBP.getCookie('acc_sscode');
        }.bind(this));
        this.items.empty();
        if(this.options.collapse) {
            this.container.getElements('.'+this.options.collapse).each(function(closeBtn){
                closeBtn.addEvent('click', function(){ this.collapse(); }.bind(this));
            }.bind(this));
        }
    },
    collapse: function(){
        if(this.current) {
            this.hide(this.current, this.current.retrieve('tabContent'));
        }
        this.current = null;
        window.scrollTo(this.container.offsetLeft, this.container.offsetTop);
    },
    onHover: function(item){
        if(!item.hasClass('hover')) { item.addClass('hover'); }
    },
    onOut: function(item){
        if(item.hasClass('hover')) { item.removeClass('hover'); }
    },
    show: function(item, content) {
        content.style.display = 'block';
        item.addClass(this.options.currentCss);
        item.fireEvent('onShow',[item, this]);
    },
    hide: function(item, content) {
        if(item.hasClass(this.options.currentCss)) item.removeClass(this.options.currentCss);
        content.style.display = 'none';
        item.fireEvent('onHide',[item, this]);
    },
    autoClick: function(item, content) {
        var newSession = LBP.getCookie('acc_sscode');
        if(!content.retrieve('hasContent') || this.session!= newSession){
            this.dynamicClick(item, content);
        } else {
            this.staticClick(item, content);
        }
    },
    staticClick: function(item, content) {
        if(this.current == item && content.style.display != 'none') return;
        if(this.current) this.hide(this.current, this.current.retrieve('tabContent'));
        this.show(item,content);
        this.current = item;
    },
    dynamicClick: function(item, content) {
        var newSession = LBP.getCookie('acc_sscode');
        if(this.current == item && newSession == this.session) return;
        this.session = newSession;
        var statefn = { onSuccess: this.onSuccess.bind(this), onFailure: this.onFailure.bind(this), onRequest: this.onRequest.bind(this) };
        var itemUrl = item.retrieve('url') || '';
        var itemCaller = item.retrieve('caller');
        if(itemUrl != '') {
            this.clicked = item;
            //LBP.Media.Links.container = content;
            var dyanmicrequest = new Request($merge({ url: itemUrl, caller: itemCaller, async: false, autoCancel: true}, statefn)).send('sendby=' + itemCaller + '&dmode=html_ajax');
        }
    },
    onSuccess: function(response) {
        //LBP.Media.Links.toggleLoading(false);
        LBP.toggleLoading(false);
        var item = this.clicked;
        var content = item.retrieve('tabContent');
        if(this.current) this.hide(this.current, this.current.retrieve('tabContent'));
        this.show(item,content);
        this.current = item;
        content.store('hasContent', true);
        LBP.distributeHTML(response);
        LBP.runProcesses();
    },
    onFailure: function() {
        //if(this.current.hasClass(this.options.overlayCss)) this.current.removeClass(this.options.overlayCss);
        LBP.toggleLoading(false);
    //    LBP.Media.Links.toggleLoading(false);
    },
    onRequest: function() {
        //this.current.addClass(this.options.overlayCss);
        LBP.toggleLoading(true);
    //    LBP.Media.Links.toggleLoading(true);
    }
});

LBP.Table = new Class({
	Implements: [Options, Events],
	options: {
		rowClick: null,
		rowSelect: null,
		rowUnselect: null,
		rowFocus: null,
		rowUnfocus: null,
		checkboxClick: null,
		selectAllValue: 'all',
		selectedClassName: 'selected',
		focusClassName: 'has-focus',
		checkboxWrapperName: 'checkbox'
	},

	initialize: function(tableID, options){
		this.setOptions(options);
		/**
		*	setup vars
		*/
		var table = $(tableID);
		if (!table){
			return;
		}

		this.table = table;
		if (this.table.retrieve('installed')){
			return;
		}
		this.table.store('installed', true);

        /**
		*	count the number of checkboxes we had and reset all of them
		*/
		var count = 0;
		this.table.getElements('tbody tr input[type=checkbox]').each(function(checkbox){
			if (checkbox.value != this.options.selectAllValue){
				count++;
			}
			checkbox.set('checked', false);
            checkbox.addEvent('click', function(){
				this.onCheckboxClick(checkbox);
			}.bind(this));
		}.bind(this));

		this.table.store('totalCheckboxes', count);
		this.table.store('totalSelected', 0);

		this.table.getElements('tbody tr').each(function(row){
			if (!row.hasClass('slave')){
				row.addEvent('click', function(){
					this.onRowClick(row);
				}.bind(this));
			}
			else {
				row.addEvent('click', function(){
					var previousRow = row.getPrevious('tr');
					this.onRowClick(previousRow);
				}.bind(this));
			}
		}.bind(this));

		this.table.store('focusRow', null);

        /**
		*	add event for the big checkbox
		*/
		var bigCheckbox = this.table.getElement('input[value=' + this.options.selectAllValue + ']');
		bigCheckbox.addEvent('click', function(){
			this.onBigCheckboxClick(bigCheckbox);
		}.bind(this));
		bigCheckbox.set('checked', false);
	},

	onBigCheckboxClick: function(bigCheckbox){
		var checked = bigCheckbox.checked ? true : false;
		if (checked){
			this.table.store('totalSelected', 0);
		}
	    this.table.getElements('tbody tr input').each(function(checkbox){
			checkbox.checked = checked;
			checkbox.fireEvent('click');
            var row = checkbox.getParent('tr');
			row.store('updating', false);
		}.bind(this));
	},

	onCheckboxClick: function(checkbox){
        var row = checkbox.getParent('tr');
        /**
        *	make this row focus
        */
		this.onRowFocus(row);

        /**
		*	make this row selected or unselected
		*/
		var checked = checkbox.checked ? true : false;
        if (checked){
			this.onRowSelect(row);
		}
		else {
			this.onRowUnselect(row);
		}

        /**
		*	store the updating status so that the row click event wont fired
		*/
		row.store('updating', true);

        /**
		*	set the big checkbox or not
		*/
        var totalCheckboxes = this.table.retrieve('totalCheckboxes');
		var totalSelected = this.table.retrieve('totalSelected');
		var bigCheckbox = this.table.getElement('input[value=' + this.options.selectAllValue + ']');
		var bigDiv = bigCheckbox.getParent('.' + this.options.checkboxWrapperName);

		if (totalSelected == totalCheckboxes){
            bigCheckbox.set('checked', true);
			bigDiv.addClass(this.options.checkboxWrapperName + '-checked');
		}
		else {
			bigCheckbox.set('checked', false);
			bigDiv.removeClass(this.options.checkboxWrapperName + '-checked');
		}

		(this.options.checkboxClick || this.checkboxClick).call(this, checkbox);
	},

	onRowClick: function(row){
		if (row.retrieve('updating')){
			row.store('updating', false);
			return;
		}

        var checkbox = row.getElement('input');
		checkbox.set('checked', !checkbox.checked);
		checkbox.fireEvent('click');

        /**
		*	remove the updating status, we have finished ^^
		*/
		row.store('updating', false);

		(this.options.rowClick || this.rowClick).call(this, row);
	},

    onRowSelect: function(row){
        /**
    	*	recount
    	*/
    	this.table.store('totalSelected', this.table.retrieve('totalSelected') + 1);
        /**
    	*	change style
    	*/
    	row.addClass(this.options.selectedClassName);
        var slaveRow = row.getNext('tr.slave');
        if (slaveRow){
        	slaveRow.addClass(this.options.selectedClassName);
		}
		(this.options.rowSelect || this.rowSelect).call(this, row);
	},

	onRowFocus: function(row){
        /**
		*	remove the current focus row
		*/
		var focusRow = this.table.retrieve('focusRow');
		if (focusRow){
			this.onRowUnfocus(focusRow);
		}

		this.table.store('focusRow', row);
		row.addClass(this.options.focusClassName);
		var slaveRow = row.getNext('tr.slave');
		if (slaveRow){
			slaveRow.addClass(this.options.focusClassName);
		}
		(this.options.rowFocus || this.rowFocus).call(this, row);
	},

	onRowUnselect: function(row){
        /**
		*	recount
		*/
    	this.table.store('totalSelected', this.table.retrieve('totalSelected') - 1);
        /**
    	*	change style
    	*/
		row.removeClass(this.options.selectedClassName);
        var slaveRow = row.getNext('tr.slave');
        if (slaveRow){
			slaveRow.removeClass(this.options.selectedClassName);
		}
		(this.options.rowUnselect || this.rowUnselect).call(this, row);
	},

	onRowUnfocus: function(row){
		row.removeClass(this.options.focusClassName);
        /**
		*	store the focus row inside table
		*/
		this.table.store('focusRow', null);
		(this.options.rowUnfocus || this.rowUnfocus).call(this, row);
	},

    rowClick: function(row){},
	rowSelect: function(row){},
	rowUnselect: function(row){},
	rowFocus: function(row){},
	rowUnfocus: function(row){},
	checkboxClick: function(checkbox){}
});

LBP.Tabs = new Class({
	Implements: [Options, Events],
	initialize: function(tabGroups){
		tabGroups.each(this.renderTabGroup);
	},
	renderTabGroup: function(tabGroup){
        if (tabGroup.retrieve('installed')){
			return;
		}
		tabGroup.store('installed', true);

		var items = tabGroup.getElements('.item');
		$each(items, function(currentItem){
			var specialClassName = currentItem.className.split(' ')[1];
			var currentClassName = 'item-current';
			var hoverClassName = 'item-hover';

            if (specialClassName){
	            currentClassName = specialClassName +  '-current';
	            hoverClassName = specialClassName + '-hover';
			}

			if (specialClassName == 'item-current'){
				currentClassName = 'item-current';
				hoverClassName = 'item-current-hover';
			}

			var currentLink = currentItem.getElement('a');

			if (currentLink){

                currentLink.addEvent('click', function(){
					currentLink.store('clicking', true);
				});

				currentItem.addEvent('click', function(){
					var otherItems = tabGroup.getElements('.item');
					$each(otherItems, function(currentOtherItem){
						currentOtherItem.removeClass('item-current-prev');
						var specialClassName = currentOtherItem.className.split(' ')[1];
						if (specialClassName && specialClassName != 'item-current'){
                        	currentOtherClassName = specialClassName +  '-current';
                        	currentOtherItem.removeClass(currentOtherClassName);
						}
						else {
							currentOtherItem.removeClass(currentClassName);
						}
					});

					currentItem.removeClass(hoverClassName);
					currentItem.addClass(currentClassName);
                    var currentPrev = currentItem.getPrevious('.item');

					if (currentPrev){
						currentPrev.addClass('item-current-prev');
					}

                    if (!currentLink.retrieve('clicking')){
						LBP.submitLink(currentLink);
					}

					currentLink.store('clicking', false);
				});

				/*if (!currentItem.hasClass(currentClassName)){*/
	            currentItem.addEvent('mouseover', function(){
					currentItem.addClass(hoverClassName);
				});
			    currentItem.addEvent('mouseout', function(){
					currentItem.removeClass(hoverClassName);
				});
				/*}*/
			}

            if (currentItem.hasClass(currentClassName)){
				var currentPrev = currentItem.getPrevious('.item');
				if (currentPrev){
					currentPrev.addClass('item-current-prev');
				}
			}

		});
	}
});

/**
*   Update: 10/29/2008 -- add vertical/center positioning, replace the old tip in menu.js
*   Example: Fansub|home/ Music|playlist - hEdge & vCenter tip
*/
LBP.Tip = new Hash({
    presets: {
        tipCss: 'ltip-content',
        idx: 900,
        opacity: 0.9,
        padding: 8,
        dumper: 'media-tip-dump',
        position: 'hEdge', //hEdge || vCenter || vEdge
        margin: 0
    },

    assign: function(objs, options){
        this.options = $merge(this.presets, options || {});
        if(!this.dump) {
                this.dump = new Element('div', { id: this.options.dumper});
                this.dump.set('styles', { position: 'absolute', zIndex: -2, opacity: 0});
                $('dummydt').adopt(this.dump);
        }
        objs.each(function(obj){
            if(!obj.retrieve('tipwidth')) {
                var tip = this.setUp(obj);
                obj.addEvents({
                    'mouseenter' : function(event){
                        this.show(obj, tip);
                    }.bind(this),
                    'mouseleave' : function(event){
                        this.hide(obj, tip);
                    }.bind(this)
                });
                obj.store('alignment', this.options.position);
                obj.store('margin', this.options.margin);
            }
        }.bind(this));
    },
    setUp: function(obj) {
        var tip = obj.getNext('div.'+this.options.tipCss);
        if(!tip) {
            tip = new Element("div", { 'class': this.options.tipCss});
            var tipinner = new Element("div", { 'class': this.options.tipCss+'-inner'});
            tip.adopt(tipinner);
            var tipCnt = obj.get('title');
            if(tipCnt !='') {
                tipinner.set('html', tipCnt);
                obj.removeProperty('title');
            } else { tip.set({   'html': 'Chưa có nội dung'}); }
            tip.inject(obj, 'after');
        }
        var width = this.options.width;
        var height = this.options.height;

        if(!width || !height) {
            tip.set('styles', { display: 'block', zIndex: this.options.idx, opacity: 0, position: 'absolute'});
            var size = tip.getSize();
            width = width || size.x;
            height = height || size.y;
        }
        tip.set('styles', { display: 'none', zIndex: this.options.idx, opacity: this.options.opacity, position: 'absolute'});
        obj.store('tipwidth', width);
        obj.store('tipheight', height);
        return tip;
    },
    show: function(obj, tip) {
        var pos = obj.getPosition();
        var size = obj.getSize();
        var winScroll = window.getScroll();
        var winSize = window.getSize();
        var tipSize = { x: obj.retrieve('tipwidth').toInt(), y: obj.retrieve('tipheight').toInt() };
        var fn = obj.retrieve('alignment')+'Align';
        pos = this[fn].run([obj, tip, winSize, winScroll, size, tipSize, pos],this);
        tip.set('styles', { top: pos.y+'px', left: pos.x+'px', display: 'block'});
    },
    vCenterAlign: function(obj, tip, winSize, winScroll, size, tipSize, pos) {
        var margin = obj.retrieve('margin');
        if((pos.y-winScroll.y) < (tipSize.y+margin)){
            pos.y = pos.y + size.y + margin;
        } else {
            pos.y = pos.y - tipSize.y - margin;
        }
        var com = (tipSize.x - size.x)/2;
        if((winSize.x-pos.x+winScroll.x) < com) {
            pos.x = pos.x + size.x;
        } else if( (pos.x-winScroll.x) >= com ) {
            pos.x = pos.x - com;
        }
        return pos;
    },
    hEdgeAlign: function(obj, tip, winSize, winScroll, size, tipSize, pos) {
        if((winSize.x-(pos.x+size.x-winScroll.x)) < tipSize.x){
            pos.x = pos.x - tipSize.x;
        } else {
            pos.x += size.x;
        }
        if((winSize.y-(pos.y+size.y/2-winScroll.y)) < tipSize.y){
            pos.y = pos.y - tipSize.y+size.y;
        }
        return pos;
    },
    hide: function(obj, tip) {
        tip.set('styles', {display: 'none'} );
    }
});

/***
 * Validator
 *
 *
 *
 */


(function(ns){

	Request.XML = new Class({
		Extends: Request,
		success: function(text, xml){
			if(Browser.Features.xpath) {
				xml.selectNodes = function(xpath){
					var nodes = [], node;
					var result = xml.evaluate(xpath, this, this.createNSResolver(xml.documentElement), XPathResult.ORDERED_NODE_ITERATOR_TYPE, null) ;
					if (result) while(node = result.iterateNext()) nodes.push(node);
					return nodes;
				};
				xml.selectSingleNode = function(xpath){
					var result = xml.evaluate(xpath, this, this.createNSResolver(xml.documentElement), 9, null);
					return (result && result.singleNodeValue) ? result.singleNodeValue : [];
				};
			}
			this.onSuccess(xml, text);
		}
	});

	ns.contains = function(v, or){
		v = $splat(v); or = $pick(or, true);
		for(var result = false, i = 0, l = v.length; i < l; i ++){
			result = ((this & v[i]) == v[i]);
			if(result && or || !result && !or) break;
		}
		return result;
	};

	ns.trim = {
		all : function(txt){return (txt || this).replace(/(^[ \s]+)|([ \s]+$)/g, '');},
		left : function(txt){return (txt || this).replace(/^[ \s]+/, '');},
		right : function(txt){return (txt || this).replace(/[ \s]+$/, '');}
	};

	ns.isDateTime = function(format, reObj){
		format = format || 'yyyy-MM-dd';
		var input = this, o = {}, d = new Date();
		var f1 = format.split(/[^a-z]+/gi), f2 = input.split(/\D+/g), f3 = format.split(/[a-z]+/gi), f4 = input.split(/\d+/g);
		var len = f1.length, len1 = f3.length;
		if(len != f2.length || len1 != f4.length) return false;
		for(var i = 0; i < len1; i ++)if(f3[i] != f4[i]) return false;
		for(var i = 0; i < len; i ++) o[f1[i]] = f2[i];
		o.yyyy = s(o.yyyy, o.yy, d.getFullYear(), 9999, 4);
		o.MM = s(o.MM, o.M, d.getMonth() + 1, 12);
		o.dd = s(o.dd, o.d, d.getDate(), 31);
		o.hh = s(o.hh, o.h, d.getHours(), 24);
		o.mm = s(o.mm, o.m, d.getMinutes());
		o.ss = s(o.ss, o.s, d.getSeconds());
		o.ms = s(o.ms, o.ms, d.getMilliseconds(), 999, 3);
		if(o.yyyy + o.MM + o.dd + o.hh + o.mm + o.ss + o.ms < 0) return false;
		if(o.yyyy < 100) o.yyyy += (o.yyyy > 30 ? 1900 : 2000);
		d = new Date(o.yyyy, o.MM - 1, o.dd, o.hh, o.mm, o.ss, o.ms);
		var reVal = d.getFullYear() == o.yyyy && d.getMonth() + 1 == o.MM && d.getDate() == o.dd && d.getHours() == o.hh && d.getMinutes() == o.mm && d.getSeconds() == o.ss && d.getMilliseconds() == o.ms;
		return reVal && reObj ? d : reVal;
		function s(s1, s2, s3, s4, s5){
			s4 = s4 || 60, s5 = s5 || 2;
			var reVal = s3;
			if(s1 != undefined && s1 != '' || !isNaN(s1)) reVal = s1 * 1;
			if(s2 != undefined && s2 != '' && !isNaN(s2)) reVal = s2 * 1;
			return (reVal == s1 && s1.length != s5 || reVal > s4) ? -10000 : reVal;
		}
	};

	var $properties = "accept,action,as,code,depend,elements,blank,expression,for,format,max,maxElement,min,minElement,operator,options,pass,pattern,prop,regex,require,rule,tips,to,toElement,trim,warn".split(',');

	var $triggers = {
		submit : 1,
		blur : 2,
		any : 3
	};

	var $regexs = {
		require : /.+/,
		email : /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,
		phone : /^((\(\d{2,3}\))|(\d{3}\-))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{6,7}(\-\d{1,4})?$/,
		mobile : /^((\(\d{2,3}\))|(\d{3}\-))?((13\d{9})|(15[389]\d{8}))$/,
		url : /^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"])*$/,
		ip : /^(0|[1-9]\d?|[0-1]\d{2}|2[0-4]\d|25[0-5]).(0|[1-9]\d?|[0-1]\d{2}|2[0-4]\d|25[0-5]).(0|[1-9]\d?|[0-1]\d{2}|2[0-4]\d|25[0-5]).(0|[1-9]\d?|[0-1]\d{2}|2[0-4]\d|25[0-5])$/,
		currency : /^\d+(\.\d+)?$/,
		number : /^\d+$/,
		zip : /^[1-9]\d{5}$/,
		qq : /^[1-9]\d{4,8}$/,
		english : /^[A-Za-z]+$/,
		username : /^[a-z]\w{3,19}$/i,
		integer : /^[-\+]?\d+$/,
		datex : /^(\d\d)[-/](\d\d)[-/](\d\d(?:\d\d)?)$/,
		timex : /^(\d\d)[-:](\d\d)$/,
		nothing: /.?/,
		'double' : /^[-\+]?\d+(\.\d+)?$/
	};

	var util = {
		numberFilter : function(input){
			return input.replace(/\D/g, '');
		},
		ipFix : function(input){
			return $regexs.ip.test(input)?input.replace(/([^\d])(\d{1,2})(?=\.|\b)/g, '$100$2') : false;
		},
		comma : /\s*[,£¬]\s*/
	};

	var AbstractMessenger = new Class({
		Implements : Options,
		options : {
			warn : null,
			tips : null,
			pass : null,
			remote : null
		},
		initialize : function(form, options){
			this.form = form;
			this.setOptions(options);
			this.bound = {
				'onSuccess' : this.showPass.bind(this),
				'onFailure' : this.showWarn.bind(this),
				'onTips' : this.showTips.bind(this)
			};
			if (this.options.initialize) this.options.initialize.call(this);
		},
		showWarn : function(o){this.process(o, 'warn');},
		showTips : function(o){o.element.title = this.chk(o, 'tips') ? o.options.tips : '';},
		showPass : function(o){if(o !== this.form) this.process(o, 'pass');},
		process : function(o, key){},
		chk : function(o, key){return o.options && o.options[key] && o.options[key] != ''}
	});

	ns.FollowMessenger = new Class({
		Extends : AbstractMessenger,
		options : {
			warn : 'msg block',
			tips : 'msg tips',
			remote : 'msg loading',
			pass : 'msg pass',
			blur : 'msg'
		},
		initialize : function(form, options){
			this.parent(form, options);
			this.bound.onRemote = this.showRemote.bind(this);
		},
		showTips : function(o, isBlur){if(o !== this.form && this.chk(o, 'tips')) o.getMessagePanel().setProperties({html : o.options.tips, 'class' : this.options[isBlur ? 'blur' : 'tips']});},
		showRemote : function(o){o.getMessagePanel().setProperties({html : 'Đang tải dữ liệu...', 'class' : this.options.remote}).setStyle('visibility', 'visible');},
		process : function(o, key){
			$splat(o).each(function(e){
				var msg = e.getMessage();
				e.getMessagePanel().setProperties({html : msg, 'class' : this.options[key]}).setStyle('visibility', msg && msg != '' ? 'visible' : 'hidden');
			}.bind(this));
		}
	});


	ns.ColorMessenger = new Class({
		Extends : AbstractMessenger,
		options : {
			warn : 'red',
			pass : 'green'
		},
		process : function(o, key){
			$splat(o).each(function(e){
				e.element.setStyle('color', this.options[key]).title = e.getMessage();
			}.bind(this));
		}
	});


	ns.ClassMessenger = new Class({
		Extends : AbstractMessenger,
		options : {
			warn : 'style-warn',
			pass : 'style-pass'
		},
		process : function(o, key){
			var ops = this.options;
			$splat(o).each(function(e){
				e.element.removeClass(ops.warn).removeClass(ops.pass).addClass(ops[key]).title = e.getMessage();
			});
		}
	});

	ns.TipsMessenger = new Class({
		Extends : AbstractMessenger,
		options : {
			position : 'top',
			offset : {x : 0, y : 0},
			'class' : 'tooltips'
		},
		showTips : function(o, isBlur){
			if(o === this.form || !this.chk(o, 'tips')) return;
			if(isBlur) {this.getDialog().setStyle('visibility', 'hidden');return;}
			this.getDialog().set('html', o.options.tips);
			this.dialog.setStyles(this.getStyles(o)).addClass(this.options.tips || '');
			this.dialog.addEvent('click', function(){
				this.getDialog().setStyle('visibility', 'hidden');
			}.bind(this));
		},
		showPass : function(){this.getDialog().setStyle('visibility', 'hidden');},
		getDialog : function(){
			return this.dialog = this.dialog || new Element('div', {'class' : this.options['class']}).inject(document.body);
		},
		getStyles : function(o){
			var ops = this.options, cd = o.element.getCoordinates();
			var size = this.dialog.getSize();
			var left = cd.left, top = cd.top;
			switch(ops.position){
				default :
					if (Browser.Engine.trident){
						top = top - 23;
					}

					if (Browser.Engine.gecko){
						if (Browser.Engine.version < 19){
							top = top + 6;
						}
						else {
							top = top + 3;
						}
					}

					top = top - size.y;
					break;
				case 'right' : left = cd.right;break;
				case 'left' : left -= size.x;break;
				case 'bottom' : top = cd.bottom;break;
			}

            if (LBP.Modal.isOpen){
				if (Browser.Engine.trident){
					top = top + 26
				}
			}

			return {left : left + ops.offset.x, top : top + ops.offset.y, visibility : 'visible'};
		},
		process : function(o, key){
			var ops = this.options,o = $splat(o)[0];
			var pos = o.element.getPosition();
			this.getDialog().set('html', o.getMessage());
			this.dialog.setStyles(this.getStyles(o)).addClass(this.options[key] || '');
		}
	});

	ns.AlertMessenger = new Class({
		Extends : AbstractMessenger,
		options : { showOne : false},
		showPass : function(o){
			if(o !== this.form) o.element.title = this.chk(o, 'pass') ? o.options.pass : '';
		},
		process : function(o, key){
			if(o.length){
				var warns = [this.form.options.title];
				o.each(function(e, i){
					warns.push((i + 1) + '.' + e.getMessage());
					e.element.title = e.getMessage();
				});
				alert(warns.join('\n'));
			} else {
				if(this.options.showOne) alert(o.getMessage());
				o.element.title = o.getMessage();
			}
		}
	});

	ns.SummaryMessenger = new Class({
		Extends : AbstractMessenger,
		options : {id : ''},
		showPass : function(o){
			if(o !== this.form) {if(this.chk(o, 'pass')) o.element.title = o.options.pass;}
			else $(o.options.summary).setStyle('display', '');
		},
		process : function(o){
			if(o.length){
				var warns = ['<div class="title">' + this.form.options.title + '</div><ol>'];
				o.each(function(e, i){
					warns.push('<li>' + e.getMessage() + '</li>');
					e.element.title = e.getMessage();
				});
				$(this.options.id).setStyle('display', 'block').set('html', warns.join('') + '</ol>');
			}
		}
	});

	var AbstractReader = new Class({
		Implements : [Events, Options],
		options : {
			wait : true
		},
		initialize : function(form, options){
			this.form = form;
			this.setOptions(options);
			if (this.options.initialize) this.options.initialize.call(this);
			if(this.options.wait) window.addEvent('domready', this.read.bind(this));
			else this.read();
		},
		read : function(){},
		fill : function(){}
	});

	ns.AttributeReader = new Class({
		Extends : AbstractReader,
		read : function(){
			this.form.getForm().getElements('input[rule], select[rule], textarea[rule]', true).each(function(el){
				if (!el.name || el.disabled) return;
				this.fill(el);
			}.bind(this));
		},
		fill : function(el){
			this.form.addElement(ns.Factory.Element.build(ns.Factory.Element.getAttributes(el)));
		}
	});

	ns.TagReader = new Class({
		Extends : AbstractReader,
		options : {
			ns : 'ui',
			name : 'v'
		},
		initialize : function(form, options){
			this.setNS(options.ns || this.options.ns);
			this.parent(form, options);
		},
		getTag : function(){
			var ops = this.options;
			return (ops.ns == '' || Browser.Engine.trident) ? ops.name : ops.ns + ':' + ops.name;
		},
		setNS : function(ns){
			if(Browser.Engine.trident) document.namespaces.add(ns, 'http://wfsr.net');
			else document.documentElement.setAttribute('xmlns:' + ns, 'http://wfsr.net');
		},
		read : function(){
			var ops = this.options;
			if (this.form.getForm()){
				$A(this.form.getForm().getElementsByTagName(this.getTag())).each(function(el, i){
					if (!el.getAttribute('for')) return;
					this.fill(el);
				}.bind(this));
			}
		},
		fill : function(el){
			var  options = ns.Factory.Element.getAttributes(el);
			options.holder = el;
			options.element = this.form.getFormItem(options['for']);
			this.form.addElement(ns.Factory.Element.build(options));
		}
	});

	ns.JsonReader = new Class({
		Extends : AbstractReader,
		options : {
			wait : false,
			url : 'rules/validation.js',
			as : 'JSON'
		},
		read : function(){
			var self = this;
			if(!Request[this.options.as]) return;
			var request = new Request[this.options.as]({url: this.options.url, onSuccess : function(data){
				window.addEvent('domready', self.wait.bind(self, data));
				request = null;
			}}).get();
		},
		wait : function(json){
			var self = this;
			json.rules.each(function(rule){
				if (!rule['for']) return;
				self.fill(rule);
			});
		},
		fill : function(rule){
			rule.element = this.form.getFormItem(rule['for']);
			this.form.addElement(ns.Factory.Element.build(rule));
		}
	});

	ns.XmlReader = new Class({
		Extends : ns.JsonReader,
		options : {
			url : 'rules/validation.xml',
			xpath : {name : 'demo', path : '/validator/index.html'},
			as : 'XML'
		},
		getXPath : function(){
			var ops = this.options;
			return $type(ops.xpath) == 'string' ? ops.xpath : '//form[@name="' + ops.xpath.name + '" and @path="' + ops.xpath.path + '"]/item';
		},
		wait : function(xml){
			var nodes = xml.selectNodes(this.getXPath()), o;
			for(var i = 0, l = nodes.length; i < l; i ++){
				if (!nodes[i].getAttribute('for')) continue;
				o = ns.Factory.Element.getAttributes(nodes[i]);
				if(nodes[i].getAttribute('apply')) o = $merge(ns.Factory.Element.getAttributes(xml.selectSingleNode(nodes[i].getAttribute('apply'))), o);
				if(o.rule) this.fill(o);
			};
		}
	});


	var Validator = ns.Validator = {
		forms : {},
		setup : function(options){
			var form = new ns.Form(options);
			this.forms[options.form] = form;
			return form;
		},
		validate : function(options){
		},
		registerElement : function(className, prototype, autoInherit){
			if(autoInherit !== false) prototype.Extends = AbstractElement;
			ns.ExtendElements[className] = new Class(prototype);
		},
		registerRegex : function(key, regex){
			$regexs[key] = regex;
		},
		registerProperty : function(){
			for(var i = 0, l = arguments.length, ps = $properties.join(','); i < l; i ++) if(!ps.contains(arguments[i], ',')) $properties.push(arguments[i]);
		},
		registerReader : function(className, prototype, autoInherit){
			if(autoInherit !== false) prototype.Extends = AbstractReader;
			ns.ExtendReaders[className] = new Class(prototype);
		},
		registerMessenger : function(className, prototype, autoInherit){
			if(autoInherit !== false) prototype.Extends = AbstractMessenger;
			ns.ExtendMessengers[className] = new Class(prototype);
		},
		toString : function(){return 'Validator ';}
	};

	ns.Factory = {
		Element : {
			build : function(o){
				var element, rule = o.rule.capitalize().replace(/Element$/, '') + 'Element';
				return new ($pick(ns[rule], ns.ExtendElements[rule], ns.RegexElement))(o);
			},
			getAttributes : function(el){
				var options = {element : el};
				$properties.each(function(prop){
					if(el.getAttribute(prop)) options[prop] = el.getAttribute(prop);
				});
				return options;
			}
		},
		Messenger : {
			build : function(name, form, options){
				name = name.capitalize().replace(/Messenger$/, '') + 'Messenger';
				return new ($pick(ns[name], ns.ExtendMessengers[name]))(form, options);
			}
		},
		Reader : {
			build : function(name, form, options){
				name = name.capitalize().replace(/Reader$/, '') + 'Reader';
				return new ($pick(ns[name], ns.ExtendReaders[name]))(form, options);
			}
		}
	};

	ns.Form = new Class({
		Implements : [Events, Options],
		options : {
			form : null,
			configs : 'tag',
			triggers : 'submit, blur',
			warns : 'follow',
			step : false,
			wait : false,
			title : 'Xin hãy sửa các lỗi sau:',
			ignoreOldEvent : true,
			events : null/*,
			onTips : $empty,
			onRemote : $empty,
			onSuccess : $empty,
			onFailure : $empty*/
		},
		initialize : function(options){
			this.isValid = false;
			this.elements = [];
			this.invalids = [];
			this.batch = false;
			this.setOptions(options);
			this.initMessenger();
			this.parseEnmValue('triggers', $triggers);
			this.bound = {onTips : this.tips.bind(this), onRemote : this.remote.bind(this), onFailure : this.failure.bind(this), onSuccess : this.success.bind(this)};
			if (this.options.initialize) this.options.initialize.call(this);
			if(this.options.wait) window.addEvent('domready', this.loadConfigs.bind(this));
			else this.loadConfigs();
		},
		validate : function(){
			this.invalids.empty();
			this.batch = true;
			for(var i = 0, l = this.elements.length; i < l; i ++) if(!this.elements[i].validate() && this.options.step) break;
			(this.isValid = this.invalids.length == 0) ? this.fireEvent('onSuccess', this) : this.fireEvent('onFailure', [this.invalids]);
			this.batch = false;
			return this.isValid;
		},
		tips : function(el, isBlur){
			this.fireEvent('onTips', [el, isBlur]);
		},
		remote : function(el){
			this.fireEvent('onRemote', el);
		},
		success : function(el){
			this.fireEvent('onSuccess', el);
		},
		failure : function(el){
			this.invalids.include(el);
			if(this.options.step || !this.batch) this.fireEvent('onFailure', el);
		},
		initMessenger : function(){
			var self = this;
			this.options.warns.split(util.comma).each(function(warn){
				warn = warn.trim().toLowerCase();
				var messenger = ns.Factory.Messenger.build(warn, self, self.options[warn] || {});
				if(messenger)  self.addEvents(messenger.bound);
			});
		},
		loadConfigs : function(){
			var self = this;
			this.options.configs.split(util.comma).each(function(config, i){
				config = config.trim().toLowerCase();
				ns.Factory.Reader.build(config, self, self.options[config] || {});
			});
			if(Browser.loaded) this._add();
			else window.addEvent('domready', this._add.bind(this));
		},
		getForm : function(){
			if(!this.form){
				this.form = $(this.options.form);
				if (this.form){
					if(ns.contains.call(this.options.triggers, $triggers.submit)){
						if(this.options.ignoreOldEvent){
							this.form.removeProperty('onsubmit');
							this.form.onsubmit = null;
						} else {
							var oldEvent = this.form.onsubmit;
						}
						this.form.addEvent('submit', function(){
							if(oldEvent) oldEvent();
							return this.validate();
						}.bind(this));
					}
				}
			}
			return this.form;
		},
		getFormItem : function(name){
			var items = this.getForm().getElements('[name=' + name + ']');
			return items.length ? items[items.length - 1] : $(name);
		},
		addElement : function(options){
			var ops = this.options;
			var element = options;
			this.elements.push(element);
			element.addEvents(this.bound);
			if(ops.events && ops.events[element.element.name]) element.addEvents(ops.events[element.element.name]);
			element.attach(ns.contains.call(ops.triggers, $triggers.blur));
		},
		add : function(o, loaded){
			if(!loaded) {
				this.rules = this.rules || [];
				this.rules.push(o);
			} else {
				if(!o.validate) o = ns.Factory.Element.build(o);
				this.addElement(o);
			}
			return this;
		},
		_add : function(){
			if(!this.rules)return;
			for(var i = 0, l = this.rules.length; i < l; i ++) this.add(this.rules[i], true);
			this.rules.empty();
			this.rules = null;
			delete this.rules;
		},
		parseEnmValue : function(prop, enums){
			var ops = this.options;
			if(typeof(ops[prop]) == 'string'){
				var properties = ops[prop];
				ops[prop] = 0;
				properties.replace(/\s/g, '').split(',').each(function(key){
					ops[prop] |= enums[key] || 0;
				});
				ops[prop] = ops[prop] || 1;
			}
		}
	});

	var AbstractElement = new Class({
		Implements : [Events, Options],
		options : {
			element : null,
			trim : 'all',
			depend : false,
			require : true,
			warn : null,
			tips : null,
			blank : null,
			pass : null,
			action : null/*,
			onTips : $empty,
			onRemote : $empty,
			onSuccess : $empty,
			onFailure : $empty*/
		},
		initialize : function(options){
			this.isValid = false;
			this.setOptions(options);
			this.options.require = this.toBoolean(this.options.require);
			this.element = $(this.options.element || this.options['for']);
			delete this.options.element;
			if (this.options.initialize) this.options.initialize.call(this);
		},
		attach  : function(validateOnBlur){
			this.bound = {focus : this.focus.bind(this), 'blur' : this.blur.bind(this, validateOnBlur)};
			if(Browser.Engine.webkit && this.element.type == 'file') {this.bound.change = this.bound.blur;delete this.bound.blur;}
			this.getMessagePanel();
			this.fireEvent('onTips', [this, true]);
			this.element.addEvents(this.bound);
		},
		focus : function(){
			this.fireEvent('onTips', this);
		},
		blur : function(validate){
			if(!validate) {this.fireEvent('onTips', [this, true]);return;}
			this.validate();
		},
		beforeRemote : function(){},
		afterRemote : function(json){
			this.isValid = (json.state == 1);
			this.$json = json;
			this.$value = this.value;
			this.fireEvent(this.isValid ? 'onSuccess' : 'onFailure', this);
		},
		remoteValidate : function(){
			if(this.isValid && this.isRemote() && this.value != (this.$value || '')) {
				this.params = {};
				this.params[this.element.name] = this.value;
				this.fireEvent('onRemote', this);
				var remote = new Request({
					url: this.options.action,
					onSuccess : function(responseJSON){
						var json = JSON.decode(responseJSON);
						this.afterRemote(json);
						remote = null;
				}.bind(this),
				onFailure : function(){
					this.afterRemote({state : 1, message : 'Không kết nối được'});
					remote = null;
				}.bind(this)}
				).post(this.params);
			}
		},
		success  : function(){
			if(this.options.require || this.value != '') {
				this.fireEvent('onSuccess', this);
			}
		},
		failure  :  function(){
			this.fireEvent('onFailure', this);
		},
		getValue : function(){
			return this.trim(this.element.get('value') || '');
		},
		getMessage : function(){
			var result = (this.$json ? this.$json.message : this.options[this.isValid ? 'pass' : (this.value === '' ? 'blank' : 'warn')]) || 'Kết quả';
			return result;
		},
		trim : function(text){
			var fn = ns.trim[this.options.trim];
			return fn ? fn.call(text) : text;
		},
		getMessagePanel : function(){
			if(!this.panel && !(this.panel = $(this.element.name + '_message'))){
				this.panel = new Element(
					'div',
					{
						id : this.element.name + '_message'
						/*,
						'styles': {
							'display': 'none'
						}*/
					}
				);
				if(this.options.holder) this.panel.injectBefore($(this.options.holder));
				else this.panel.injectAfter(this.element);
			}
			return this.panel;
		},
		validate : function(){
			if(this.element.disabled) return true;
			var ops = this.options;
			this.value = this.getValue();
			if(!(this.$value != undefined && this.value == this.$value)){
				if(ops.depend){try{eval(ops.depend);}catch(e){alert(e);}};
				this.isValid = !ops.require && this.value == '';
				if(!this.isValid){
						this.$json = null;
						this.isValid = this.process();
						if(this.isValid && this.isRemote()) this.remoteValidate();
				}
			}
			this.$value = this.value;
			this.isValid ? this.success() : this.failure();
			return this.isValid;
		},
		isRemote : function(){
			return this.options.action && this.options.action != '';
		},
		process : function(){
			return false;
		},
		toBoolean : function(i){
			return i === true || i === false ? i : 'no,0,false'.indexOf(i.toString().toLowerCase()) == -1;
		}
	});

	ns.RegexElement = new Class({
		Extends : AbstractElement,
		options : {
			pattern : null,
			options : ''
		},
		process : function(){
			this.pattern = this.options.pattern && new RegExp(this.options.pattern, this.options.options) || $regexs[this.options.rule];
			return this.pattern != null && this.pattern.test(this.value);
		}
	});

	ns.PasswordElement = new Class({
		Extends : AbstractElement,
		options : {
			level : 2/*,
			onLevelChange : $empty*/
		},
		initialize : function(options){
			arguments.callee.parent(options);
			this.element.addEvent('keyup', this.checkLevel.bind(this));
			this.level = 0;
		},
		checkLevel : function(){
			this.value = this.getValue();
			this.getLevel();
		},
		getLevel : function(){
			var v = this.value, l = v.length, min = 6, level = 0;
			if(l < min){
				if(l > 0)	level = 1;
			} else {
				if(/^(\d{6,9}|[a-z]{6,9}|[A-Z]{6,9})$/.test(v)) level = 1;
				else if(/^[^a-z\d]{6,8}$/i.test(v) || !/^(\d{6,9}|[a-z]{6,9}|[A-Z]{6,9})$/.test(v)) level = 2;
				if(!/^(([A-Z]*|[a-z]*|\d*|[-_\~!@#\$%\^&\*\.\(\)\[\]\{\}<>\?\\\/\'\"]*)|.{0,5})$|\s/.test(v)) level = l < 10 ? 3 : 4;
			}
			if(this.leve != level) this.fireEvent('onLevelChange', [level, this]);
			return this.level = level;
		},
		process : function(){
			return this.getLevel() >= this.options.level;
		}
	});

	ns.RangeElement = new Class({
		Extends : AbstractElement,
		options : {
			min : 0,
			minElement : null,
			max : 1000,
			maxElement : null,
			format : 'yyyy-MM-dd',
			as : 'number'
		},
		types : {
			string : function(){this.options.format = null;this.min = this.options.min;this.max = this.options.max;},
			caseinsensitivestring : function(){this.value = this.value.toUpperCase();this.min = this.options.min.toUpperCase();this.max = this.options.max.toUpperCase();},
			number : function(){this.value = this.value*1;this.min = this.options.min*1;this.max = this.options.max * 1;},
			datetime : function(){this.value = ns.isDateTime.call(this.value, this.options.format, true);this.min = ns.isDateTime.call(this.options.min, this.options.format, true);this.max = ns.isDateTime.call(this.options.max, this.options.format, true);},
			ip : function(){this.value = util.ipFix(this.value);this.min = util.ipFix(this.options.min);this.max = util.ipFix(this.options.max);}
		},
		process : function(){
			var ops = this.options;
			if(ops.minElement != null) ops.min = $(ops.minElement).value;
			if(ops.maxElement != null) ops.max = $(ops.maxElement).value;
			this.types[ops.as.toLowerCase()].call(this);
			return (this.value === false || this.min === false || this.max === false) ? false : this.min <= this.value && this.value <= this.max;
		}
	});

	ns.IdCardElement = new Class({
		Extends : AbstractElement,
		options : {
			length : 'both'
		},
		process : function(){
			if(this.options.length == 18 && 18 != this.value.length) return false;
			var number = this.value.toLowerCase();
			var d, sum = 0, v = '10x98765432', w = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2], a = '11,12,13,14,15,21,22,23,31,32,33,34,35,36,37,41,42,43,44,45,46,50,51,52,53,54,61,62,63,64,65,71,81,82,91';
			var re = number.match(/^(\d{2})\d{4}(((\d{2})(\d{2})(\d{2})(\d{3}))|((\d{4})(\d{2})(\d{2})(\d{3}[x\d])))$/);
			if(re == null || a.indexOf(re[1]) < 0) return false;
			if(re[2].length == 9){
				number = number.substr(0, 6) + '19' + number.substr(6);
				d = ['19' + re[4], re[5], re[6]].join('-');
			} else d = [re[9], re[10], re[11]].join('-');
			if(!ns.isDateTime.call(d, 'yyyy-MM-dd')) return false;
			for(var i = 0;i < 17; i++) sum += number.charAt(i) * w[i];
			return (re[2].length == 9 || number.charAt(17) == v.charAt(sum % 11));
		}
	});

	ns.RepeatElement = new Class({
		Extends : AbstractElement,
		options : {
			to : null
		},
		process : function(){
			return this.options.to && this.value == $(this.options.to).value;
		}
	});

	ns.DateTimeElement = ns.DateElement = ns.TimeElement = new Class({
		Extends : AbstractElement,
		options : {
			format : 'yyyy-MM-dd'
		},
		process : function(){
			return ns.isDateTime.call(this.value, this.options.format);
		}
	});

	ns.GroupElement = new Class({
		Extends : ns.RangeElement,
		options : {
			as : 'number',
			elements : '',
			iterates : [],
			prop : ''
		},
		attach  : function(validateOnBlur){
			arguments.callee.parent(validateOnBlur);
			var ops = this.options;
			if(ops.elements) {
				ops.iterates = $$(ops.elements.split(util.comma).join(',').replace(/\b([^\b#,]+)/g, '#$1'));
				if(!ops.prop) ops.prop = 'value';
			}
			else {
				if(this.element.options) {
					ops.iterates = this.element.options;
					if(!ops.prop) ops.prop = 'selected';
				}
				else {
					ops.iterates = document.getElementsByName(ops['for']);
					if(!ops.prop) ops.prop = 'checked';
				}
			}
			if(!validateOnBlur)return;
			if(this.element.getTag() != 'input')return;
			for(var i = ops.iterates.length - 2; i > -1; i --) $(ops.iterates[i]).addEvents(this.bound);
		},
		getValue : function(){
			var count = 0, ops = this.options;
			for(var i = ops.iterates.length - 1; i > -1; i --){
				if(ops.iterates[i][ops.prop]) count ++;
			}
			return count;
		}
	});

	ns.LimitElement = new Class({
		Extends : ns.RangeElement,
		options : {as : 'number'},
		getValue : function(){
			return arguments.callee.parent().length;
		}
	});

	ns.LimitBElement = new Class({
		Extends : ns.RangeElement,
		options : {as : 'number'},
		getValue : function(){
			return arguments.callee.parent().replace(/[^\x00-\xff]/g,"**").length;
		}
	});

	ns.FilterElement = new Class({
		Extends : AbstractElement,
		options : {
			accept : null,
			as : 'file'
		},
		types : {
			file : function(){
				return this.options.accept != null && new RegExp("^.+\\.(?=EXT)(EXT)$".replace(/EXT/g, this.options.accept.split(util.comma).join("|")), "gi").test(this.value);
			},
			badword : function(){
				return !this.types.keyword.call(this);
			},
			keyword : function(){
				return this.options.accept != null && new RegExp(this.options.accept.split(util.comma).join("|"), "gi").test(this.value);
			},
			beginwith : function(){
				return this.options.accept != null && new RegExp("^(?=EXT)(EXT)".replace(/EXT/g, this.options.accept.split(util.comma).join("|")), "gi").test(value);
			},
			endwith : function(){
				return this.options.accept != null && new RegExp(".*(?=EXT)(EXT)$".replace(/EXT/g, this.options.accept.split(util.comma).join("|")), "gi").test(value);
			}
		},
		process : function(){
			return this.types[this.options.as.toLowerCase()].call(this);
		}
	});

	ns.CustomElement = new Class({
		Extends : AbstractElement,
		options : {
			as : 'regex'
		},
		initialize : function(options){
			arguments.callee.parent(options);
			if(this.options.as.toLowerCase() == 'elements') this.init();
		},
		types : {
			regex : function(){
				var reVal = false;
				try{
					reVal = new RegExp(this.options.regex, this.options.options).test(this.value);
				} catch(e) { reVal = false;alert(['Có lỗi', e.description].join('\n'));}
				return reVal;
			},
			script : function(){
				var reVal = false;
				try{
					eval(this.options.code);
				} catch(e){ reVal = false;alert(['Có lỗi', e.description].join('\n'));}
				return reVal;
			},
			elements : function(){
				var result = this.expression;
				for(var i = this.elements.length - 1; i > -1; i--){
					result = result.replace(new RegExp('\\{' + i + '\\}', 'g'), this.elements[i].validate());
				}
				return eval(result);
			}
		},
		init : function(){
			var ops = this.options, elements = ops.expression.split(/[^a-z\d]+/ig).sort();
			this.expression = ops.expression;
			delete ops.elements;
			this.elements = [];
			for(var i = 0, l = elements.length; i < l; i ++){
				if(i > 0 && elements[i] == elements[i-1]) continue;
				var o = {}, e;
				for(e in ops){if(typeof(ops[e]) == "string")o[e] = ops[e];}
				o.rule = elements[i];
				this.elements.push(ns.Factory.Element.build(o));
				this.expression = this.expression.replace(new RegExp(elements[i], "gi"), '{' + (this.elements.length - 1) + '}');
			}
		},
		process : function(){
			return this.types[this.options.as.toLowerCase()].call(this);
		}
	});

	ns.CompareElement = new Class({
		Extends : AbstractElement,
		options : {
			to : null,
			toElement : null,
			format : 'yyyy-MM-dd',
			as : 'number',
			operator : 'equal'
		},
		types : {
			string : function(){this.options.format = null;this.to = this.options.to;},
			caseinsensitivestring : function(){this.value = this.value.toUpperCase();this.to = this.options.to.toUpperCase();},
			number : function(){this.value = this.value*1;this.to = this.options.to*1},
			datetime : function(){this.value = ns.isDateTime.call(this.value, this.options.format, true);this.to = ns.isDateTime.call(this.options.to, this.options.format, true);},
			ip : function(){this.value = util.ipFix(this.value);this.to = util.ipFix(this.options.to);}
		},
		operators : {
			notequal : function(){return this.value != this.to;},
			greaterthan : function(){return this.value > this.to;},
			greaterthanequal : function(){return this.value >= this.to;},
			lessthan : function(){return this.value < this.to;},
			lessthanequal : function(){return this.value <= this.to;},
			equal : function(){return this.value == this.to;}
		},
		process : function(){
			if(this.options.toElement != null) this.options.to = $(this.options.toElement).value;
			this.types[this.options.as.toLowerCase()].call(this);
			return !(this.value === false || this.to === false) && this.operators[this.options.operator.toLowerCase()].call(this);
		}
	});
	ns.ExtendElements = {};
	ns.ExtendMessengers = {};
	ns.ExtendReaders = {};

})(LBPX = {});

