var BaseClass = new Class({ 
	
	Implements: [Options],
	
    initialize: function (options) {
        this.setOptions(options);
    },
    
    visibleElement: function(element, visible) {
    	var elementObj = $(element);

		if (elementObj.length){
            if (visible) {
                $(elementObj).show();
            } else {
                $(elementObj).hide();
            }
        } else {
            throw('visibleElement: Element ' + element + ' is not found.');
        }
    },
    
    enableElement: function(element, enable) {
        try {
            element = $(element);
            
            if (this.isDisabled(element)) {
                element.removeAttr('disabled');
            } else {
                element.attr('disabled', 'disabled')
            }
        } catch(ex) {
            this.showAlert('enableElement: Element ' + element + ' is not found.');
        }
    },
    
    isDisabled: function(element) {
        return $(element).is(':disabled') || element.attr('disabled') == 'disabled';
    },
    
    showAlert: function(message) {
        alert(message);
    }
});

/*
 * Abstract class for ajax forms
 */
var AjaxForm = new Class({ 

	Extends: BaseClass, 
	
	options: {
            submitParams:{isAsyncPostBack: '1'},
            cssClassError: "errorMessage",
            cssClassSuccess: "successMessage",
            cssClassInvalidField: 'invalid',
            cssClassErrorIcon: 'icon-error',
            actionName:'wumode',
            formId: null,
            messageId: null
    },
	
    initialize: function (options) {
    	this.parent(options);
        this.initForm(options);
    },

    initForm: function(options) {
        this.setOptions(options);
        this.initEventHandlers();
        this.asyncRequestIndicator = null;
        this.addEvents();
        this.onFormInitialized();
    },

    initEventHandlers: function() {
        //this.onSuccessHandler = this.onSuccess.bindAsEventListener(this);
        //this.onFailedHandler = this.onFailed.bindAsEventListener(this);
        //this.onCompleteHandler = this.onComplete.bindAsEventListener(this);
        //this.onSubmitHandler = this.onSubmit.bindAsEventListener(this);

        this.onSuccessHandler = jQuery.proxy(this.onSuccess, this);
        this.onFailedHandler = jQuery.proxy(this.onFailed, this);
        this.onCompleteHandler = jQuery.proxy(this.onComplete, this);
        this.onSubmitHandler = jQuery.proxy(this.onSubmit, this);
    },
    
    addEvents: function() {
        if (this.options.formId) {
           	//Event.observe(this.options.formId, 'submit', this.onSubmitHandler)
			var form = $(this.options.formId);
			if (form.length) {
            	form.submit(this.onSubmitHandler);
			}
        }
    },
    
    removeEvents: function() {
        if (this.options.formId) {
			var form = $(this.options.formId);
			if (form.length) {
        		form.unbind();
        	}
            //Event.stopObserving(this.options.formId, 'submit', this.onSubmitHandler)
        }
    },

    initUpdatingIndicator: function() {
        var container;
        if (this.options
            && this.options.indicatorPlaceholderId
            && this.options.indicatorPlaceholderId != "auto")
        {
            container = this.options.indicatorPlaceholderId;
        } else if (this.options.formId){
            //var elements = $(this.options.formId).getElementsBySelector('textarea');
            var elements = $(this.options.formId + ' textarea');
            if (elements && elements.length) {
                if (elements[0].id != "")
                    container = elements[0].id;
                else
                    container = elements[0];
            }
        }

		if (container && this.asyncRequestIndicator == null) {
	        this.asyncRequestIndicator = new UpdateExtender({
	            panelId: this.options.formId,
	            containerId: container
	        });
		}
    },
    
    showInvalidField: function(field, message) {
        var form = $(this.options.formId);
        //var fieldObjs = form.getInputs(null, field);
        var fieldObjs = form.find('input[name='+field+']');
        
        if (fieldObjs && fieldObjs.length) {
            // highlight field as invalid
//            var fieldObj = fieldObjs[0];
            fieldObjs.addClass(this.options.cssClassInvalidField);
            
            // add error icon
            var icon = document.createElement('DIV');
            icon.className = this.options.cssClassErrorIcon;
            icon.title = message;
            //Element.insert(fieldObj, { after: icon});
            fieldObjs.after(icon);
        }
    },
    
    clearPreviuosResults: function(form) {
        if (!form) {
            form = $(this.options.formId);
        }
        
        form.find('div.' + this.options.cssClassErrorIcon).remove();
        
        /*var elements = form.getInputs();
        for (var i = 0; i < elements.length; i++) {
            elements[i].removeClass(this.options.cssClassInvalidField);
            $(elements[i]).
            var icon = elements[i].next('div.' + this.options.cssClassErrorIcon);
            if (icon) {
                icon.remove();
            }
        }*/
        
        if (this.options.messageId) {
            var panel = $(this.options.messageId);
            if (panel) {
                panel.empty('');
            }
        }
    },
    
    showMessage: function(messages) {
        if (   (typeof(messages) == 'string' && messages.length == 0) 
            || (jQuery.isArray(messages) && messages.length == 0) ) {
            return;
        }
        
        var panel, message = '';
        if (this.options.messageId) {
            panel = $(this.options.messageId);
        }
        
        if (panel) {
            if (typeof(messages) == 'string') {
                message = messages;
            } else {
                var message = '';
                for(var i = 0; i < messages.length; i++) {
                    message += this.__parseMessageItem(messages[i], '<li>', '</li>');
                }
                if (message) {
                    message = '<ul>' + message + '</ul>';
                }
            }
            
            if (message) {
                panel.html(message);
            }
        } else {
            if (typeof(messages) == 'string') {
                message = messages;
            } else {
                for(var i = 0; i < messages.length; i++) {
                    message += this.__parseMessageItem(messages[i], '', '\r\n');
                }
            }
            
            // is string is not empty
            if (message.length != 0) {
                alert(message);
            }
        }
    },
    
    setMessageStyle: function(className) {
        if (this.options.messageId) {
            var panel = $(this.options.messageId);
            if (panel.length > 0) {
                panel.removeClass(this.options.cssClassError);
                panel.removeClass(this.options.cssClassSuccess);
                panel.addClass(className);
            }
        }
    },

    show: function(senderObj) {
        $(this.options.formId).show();
    },

    hide: function(senderObj) {
        $(this.options.formId).hide();
    },

    visible: function() {
        //return Element.visible(this.options.formId);
        $(this.options.formId).is(':visible');
    },

    toggle: function(senderObj, evtObj) {
        if (this.visible()) {
            this.hide(senderObj);
            this.clearPreviuosResults();
        } else {
            if (this.onBeforeShow && typeof(this.onBeforeShow) == 'function')
                this.onBeforeShow();
            this.show(senderObj);
        }
    },

    enableForm: function() {
        var form = $(this.options.formId);
        if (form) {
            try {
            	form.find('input, select').removeAttr('disabled');
                //Form.enable(form);
            } catch(e) {}
        }
    },

    disableForm: function() {
        var form = $(this.options.formId);
        if (form) {
            //Form.disable(form);
            form.find('input, select').attr('disabled', 'disabled');
        }
    },
    
    resetForm: function() {
        var form = $(this.options.formId);
        if (form) {
            //Form.reset(form);
            //form.get(0).reset();
            form.resetForm();
        }
    },

    fillSubmitParams: function(ajaxForm) {
        var action;
        var buttons = $(ajaxForm).find(':submit');
        
        if (buttons && buttons.length == 1) {
             action = buttons[0].name;
        }
        
        if (!action) {
            action = ajaxForm.attr(this.options.actionName);
        }
        
        this.options.submitParams[this.options.actionName] = action;
    },

    onSubmit: function(eventObj) {
        try {
            // stop submit event
            if (eventObj) { //  && eventObj.preventDefault
                //Event.stop(eventObj);
                eventObj.stopPropagation();
            }

            var ajaxForm = $(this.options.formId);
            this.clearPreviuosResults(ajaxForm);

            if (typeof(this.onBeforeSubmit) == 'function') {
                this.onBeforeSubmit(ajaxForm, options);
            }
            
			this.options.submitParams = ajaxForm.serializeObject();
            this.fillSubmitParams(ajaxForm);
            
            /*var options = {
                parameters: this.options.submitParams,
                method: 'post',
                evalJSON: true,
                onException: this.onFailedHandler,
                onFailure: this.onFailedHandler,
                onSuccess: this.onSuccessHandler,
                onComplete: this.onCompleteHandler
            };
            
            ajaxForm.request(options);*/
            var options = {
                data: this.options.submitParams,
                dataType: 'json',
                method: 'post',
                error: this.onFailedHandler,
                success: this.onSuccessHandler,
                complete: this.onCompleteHandler
            };
            ajaxForm.ajaxSubmit(options); 
            
            if (typeof(this.onAfterSubmit) == 'function') {
                this.onAfterSubmit(ajaxForm, options);
            }
        } catch(e) {
            alert("Error: " + e.message);
        }
        return false;
    },

    onSuccess: function(transport, json) {

    },

    onFailed: function(transport, exObj) {

    },
    
    onComplete: function() {
    },
    
    onFormInitialized: function() {
        
    },
    
    __parseMessageItem: function(item, prefix, postfix) {
        var message = '';
        if (typeof(item) == 'object') {
            if (item.key) {
                this.showInvalidField(item.key, item.message);
            } else if (item.message) {
                message += prefix + item.message + postfix;
            }
        } else if(typeof(item) == 'string'){
            message += prefix + item + postfix;
        }
        
        return message;
    },
    
    __handleResponseMessages: function(responseObj) {
        if (!responseObj.success) {
            this.showMessage(responseObj.errors);
            this.setMessageStyle(this.options.cssClassError);
        } else if (responseObj.message) {
            this.showMessage(responseObj.message);
            this.setMessageStyle(this.options.cssClassSuccess);
        } else {
            
        }
    }
}); 

var WebService = new Class({

	Extends: BaseClass,

    options: {
		submitParams:{isAsyncPostBack: '1'},
		serviceUrl: null,
		alertPrefix: ''
    },
    
    initialize: function (options) {
    	this.parent(options);
        this.initService(options);
    },
    
    initService: function(options) {
        this.initEventHandlers();
        this.requestMethod = 'GET'; //'POST'
    },
    
    initEventHandlers: function() {
        this.onSuccessHandler = jQuery.proxy(this.onSuccess, this);
        this.onFailedHandler = jQuery.proxy(this.onFailed, this);
        this.onCompleteHandler = jQuery.proxy(this.onComplete, this);
        //this.onBeforeRequestHandler = this.onBeforeRequest.bindAsEventListener(this);
        this.onRequestHandler = jQuery.proxy(this.onRequest, this);
    },
    
    request: function() {
        this.onRequest();
    },
    
    onRequest: function() {
        this.onBeforeRequest();
        //new Ajax.Request(this.options.serviceUrl, {   
        $.ajax({
        	'url': this.options.serviceUrl,
            'method': this.requestMethod,
            'data': this.options.submitParams,
            'dataType': 'json',
            'error': this.onFailedHandler,
            'success': this.onSuccessHandler,
            'complete': this.onCompleteHandler
        });
    },
    
    onBeforeRequest: function() {  
    },
    
    onSuccess: function(transport) { 
        //if (transport.responseText.isJSON()) {
        if (transport) {
            //var responseObj = transport.responseText.evalJSON();
            //this.onResponseReceived(responseObj);
            this.onResponseReceived(transport);
        } else {
            this.onFailed(transport);
        }
    },
    
    // virtual method
    onResponseReceived: function(transport) {
    },
    
    // virtual method
    onFailed: function(transport) { 
    },
    
    onComplete: function(transport) { 
    },
    
    showResponseErrors: function(responseObj) {
        if (responseObj.errors) {
            // ToDo: parse error messages
            this.showAlert(responseObj.errors[0]);
        } else if (responseObj.message) {
            this.showAlert(responseObj.message);
        }
    }
});

var Timer = new Class({ 

	Implements: [Options],
	
	options: {
		tickInterval:1, // seconds
		//timerTpl: new Template("#{hours}:#{minutes}:#{seconds}")
		timerTpl: $.template( null, "${hours}:${minutes}:${seconds}")
    },
	
    initialize: function (options) {
        this.setOptions(options);
        //this.onTickHandler = this._onTick.bindAsEventListener(this);
        this.onTickHandler = jQuery.proxy(this._onTick, this);
        // readonly property
        this.isStarted = false;
    },
    
    setTickInterval: function(interval) {
        interval = parseInt(interval);
        if (Object.isNumber(interval) && interval > 0) {
            this.options.tickInterval = interval;
        }
    },
    
    setTickHandler: function(tickHandler) {
        if (typeof(tickHandler) == 'function') {
            this.onTick = tickHandler;
        }
    },
    
    start: function(tickHandler, tickInterval) {
        this.stop();
        delete this.executer;
        
        this.setTickHandler(tickHandler);
        this.setTickInterval(tickInterval);
        
        //this.executer = new PeriodicalExecuter(this.onTickHandler, this.options.tickInterval);
        this.executer = $.timer(this.options.tickInterval * 1000, this.onTickHandler);
        this.isStarted = true;
    },
    
    stop: function() {
        if (this.executer && this.isStarted) {
            this.executer.stop();
            this.isStarted = false;
        }
    },
    
    getCurrentUnixTimeStamp: function() {
        var dateObj = new Date();
        return Math.round(dateObj.getTime() / 1000); // in seconds
    },
    
    getLeftSeconds: function(startTime, seconds) {
        return seconds - (this.getCurrentUnixTimeStamp() - startTime);
    },
    
    getFormattedTime: function(timeObj) {
        var hours = this._checkZero(timeObj.getUTCHours());
        var minutes = this._checkZero(timeObj.getMinutes());
        var seconds = this._checkZero(timeObj.getSeconds());
            
        //return this.options.timerTpl.evaluate({'hours':hours,'minutes':minutes,'seconds':seconds});
        return $.tmpl(this.options.timerTpl, {'hours':hours,'minutes':minutes,'seconds':seconds});
    },
    
    _onTick: function() {
        if (typeof(this.onTick) == 'function') {
            this.onTick(this);
        }
    },
    
    _checkZero: function(value) {
        if (value < 10 && value >= 0)
            value = '0' + value;
        return value;
    }
});

Object.isNumber = function(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

Object.isArray = function(val) {
	return $.isArray(val);
}

Object.intval = function (value, base) {
    // http://kevin.vanzonneveld.net
    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: stensi
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // *     example 1: intval('Kevin van Zonneveld');
    // *     returns 1: 0
    // *     example 2: intval(4.2);
    // *     returns 2: 4
    // *     example 3: intval(42, 8);
    // *     returns 3: 42
    // *     example 4: intval('09');
    // *     returns 4: 9
 
    var tmp;
    var type = typeof( value );

    if(type == 'boolean'){
        if (value == true) {
            return 1;
        } else {
            return 0;
        }
    } else if(type == 'string'){
        tmp = parseInt(value * 1);
        if(isNaN(tmp) || !isFinite(tmp)){
            var matched = value.match(/\d+/);
            if (matched && matched[0]) {
                return matched[0];
            }
            return 0;
        } else{
            return tmp.toString(base || 10);
        }
    } else if(type == 'number' && isFinite(value) ){
        return Math.floor(value);
    } else{
        return 0;
    }
}

$.fn.serializeObject = function()
{
   var o = {};
   var a = this.serializeArray();
   $.each(a, function() {
       if (o[this.name]) {
           if (!o[this.name].push) {
               o[this.name] = [o[this.name]];
           }
           o[this.name].push(this.value || '');
       } else {
           o[this.name] = this.value || '';
       }
   });
   return o;
};
$.fn.enable = function()
{
	this.attr('disabled', 'disabled');
	return this;
};
$.fn.disable = function()
{
	this.removeAttr('disabled');
	return this;
};
