// Metodos
jQuery.each( {
    
    /*
	 * Alinea un layer, con offsets
	 */
    sahAlign: function(sLeftRight, sTopBottom, nXOffset, nYOffset)
    {
	    /*
		 * TODO: Calcular el ancho y alto reales del layer para no estar
		 * utilizando css padding, css border, etc...
		 */
	    var nX = nY = 5;
	    if(sLeftRight == undefined)
		    sLeftRight = 'left';
	    if(sTopBottom == undefined)
		    sTopBottom = 'top';
	    if(nXOffset == undefined)
		    nXOffset = 5;
	    if(nYOffset == undefined)
		    nYOffset = nXOffset;
	    this.each(function(i)
	    {
		    if(sLeftRight == 'left')
			    nX = nXOffset + $(window).scrollLeft();
		    else nX =
		        $(window).width() - jQuery(this).width()
		            - parseInt(jQuery(this).css('padding-left'), 10)
		            - parseInt(jQuery(this).css('padding-right'), 10) - nXOffset
		            + $(window).scrollLeft();
		    if(sTopBottom == 'top')
			    nY = nYOffset + $(window).scrollTop();
		    else nY =
		        $(window).height() - jQuery(this).height()
		            - parseInt(jQuery(this).css('padding-top'), 10)
		            - parseInt(jQuery(this).css('padding-bottom'), 10) - nYOffset
		            + $(window).scrollTop();
		    
		    /* actualizar css */
		    jQuery(this).css( {
		        position: 'absolute',
		        top: nY + 'px',
		        left: nX + 'px'
		    });
	    });
	    return this;
    },
    
    /*
	 * Remueve el atributo "disabled" de los elementos coincidentes
	 */
    sahEnable: function()
    {
	    return this.removeAttr('disabled');
    },
    /*
	 * Aplica el atributo "disabled" a los elementos coincidentes
	 */
    sahDisable: function()
    {
	    return this.attr('disabled', 'disabled');
    },
    /*
	 * Extiende una capa al tamaño total del documento, util para usar cortinas
	 */
    sahExpand: function(Options)
    {
	    Options = jQuery.extend( {
		    fade: true
	    }, Options);
	    this.css( {
	        position: 'absolute',
	        top: '0px',
	        left: '0px',
	        width: jQuery(document).width() + 'px',
	        height: jQuery(document).height() + 'px'
	    });
	    if(Options.fade)
		    this.fadeIn('slow');
	    else this.show();
	    return this;
    },
    /*
	 * Centra los elementos en pantalla
	 */
    sahCenter: function()
    {
	    /* primero absolutize y luego modificamos su top y left */
	    this.css('position', 'absolute').css( {
	        top: (jQuery(window).height() - this.height()) / 2 + jQuery(window).scrollTop() + 'px',
	        left: (jQuery(window).width() - this.width()) / 2 + jQuery(window).scrollLeft() + 'px'
	    });
	    return this;
    },
    /*
	 * Carga los selects con opciones desde una peticion ajax
	 */
    sahLoadOptions: function(MethodOptions)
    {
    	/* extend default properties object */
	    MethodOptions = jQuery.extend( {
	        value_key: 0,
	        text_key: 1,
	        global: true,
	        beforeSend: function()
	        {
	        },
	        complete: function()
	        {
	        },
	        url: '',
	        data: {},
	        default_val: null
	    }, MethodOptions);
	    /* only if exists elements to fill */
	    if(this.length > 0)
	    {
		    var jQueryObj = this;
		    jQuery.ajax( {
		        global: MethodOptions.global,
		        url: MethodOptions.url,
		        data: MethodOptions.data,
		        beforeSend: function()
		        {
			        if(jQuery.isFunction(MethodOptions.beforeSend))
			        {
				        /*
						 * Incrusta el callback como metodo del elemento y lo
						 * dispara
						 */
				        jQueryObj.each(function(i)
				        {
					        this.sahLoadOptions_beforeSend_callback = MethodOptions.beforeSend;
					        this.sahLoadOptions_beforeSend_callback();
				        });
			        }
			        jQueryObj.attr('disabled', 'disabled');
		        },
		        success: function(Options, st)
		        {
		        	/* clean and fill selects elements with options */
			        jQueryObj.empty();
			        jQuery.each(Options, function(j, Opt)
			        {
				        jQueryObj.append(jQuery('<option>').text(Opt[MethodOptions.text_key]).val(
				            Opt[MethodOptions.value_key]));
			        });
			        /* set default option selected */
			        if(MethodOptions.default_val != null)
				        jQueryObj.val(MethodOptions.default_val);
		        },
		        complete: function(Xhr, st)
		        {
		        	/* enable select elements */
			        jQueryObj.removeAttr('disabled');
			        if(jQuery.isFunction(MethodOptions.complete))
			        {
				        /*
						 * Incrusta el callback como metodo del elemento y lo
						 * dispara
						 */
				        jQueryObj.each(function(i)
				        {
					        this.sahLoadOptions_callback = MethodOptions.complete;
					        this.sahLoadOptions_callback(Xhr, st);
				        });
			        }
		        }
		    });
	    }
	    return this;
    },
    /*
	 * Aplica $.trim() y $.sah.removeExtraSpaces() al valor retornado por val()
	 * para cada elemento Devuelve el objeto jQuery
	 */
    sahTrimVal: function()
    {
	    this.each(function(i)
	    {
		    jQuery(this).val(jQuery.sah.removeExtraSpaces(jQuery(this).val()));
	    });
	    return this;
    },
    /*
	 * Devuelve TRUE si el primer elemento tiene un valor númerico, de lo
	 * contrario devuelve FALSE
	 */
    sahHasNumberVal: function()
    {
	    // "\D" Non-digit character
	if(jQuery(this[0]).val().match(/\D/) == null)
		return true;
	return false;
	
},
/*
 * Devuelve TRUE si alguno de los elementos coincidentes tiene un valor
 * alfanumerico, de lo contrario devuelve FALSE Si se especifica bEnie = true el
 * caracter Ñ entra como caracter valido.
 */
sahHasAlphanumVal: function(bEnie, bSpaces)
{
	if(bEnie == undefined)
		bEnie = false;
	if(bSpaces == undefined)
		bSpaces = false;
	var Regx = new RegExp('[^a-z0-9' + (bEnie ? 'ñ' : '') + (bSpaces ? ' ' : '') + ']', 'i');
	for( var i = 0; i < this.length; i++)
	{
		if(jQuery(this[i]).val().match(Regx) == null)
			return true;
	}
	return false;
},
/*
 * Devuelve TRUE si el alguno de los elementos coincidentes tiene un valor
 * vacio, de lo contrario devuelve FALSE Se puede especificar el parametro
 * bSahTrimVal = true para aplicar sahTrimVal() al los elementos coincidentes
 * antes de realizar la verificacion
 */
sahHasEmptyVal: function(bSahTrimVal)
{
	if(bSahTrimVal == undefined)
		bSahTrimVal = true;
	if(bSahTrimVal == true)
		this.sahTrimVal();
	
	for( var i = 0; i < this.length; i++)
		if(jQuery(this[i]).val() == '')
			return true;
	return false;
}
}, function(i)
{
	jQuery.fn[i] = this;
});

// Funciones estaticas
jQuery.sah =
    {
        /*
		 * Elimina los multiples espacios en blanco de un string Devuelve el
		 * string procesado
		 */
        removeExtraSpaces: function(sStr)
        {
	        return jQuery.trim(sStr.replace(/(\s){2,}/gm, "$1"));
        },
        /*
		 * Redirecciona a una URL valida usando SAHIB_APP_URL como base
		 */
        go: function(sUrl)
        {
	        if(sUrl == undefined)
		        sUrl = '';
	        window.location.href = SAHIB_APP_URL + '/' + sUrl;
        },
        /*
		 * Devuelve una URL valida Si se ignora sController se toma por default
		 * el valor de SAHIB_CONTROLLER_NAME
		 */
        actionUrl: function(sController, sAction)
        {
	        if(sController != undefined && sAction == undefined)
	        {
		        sAction = sController;
		        sController = SAHIB_CONTROLLER_NAME;
	        }
	        return SAHIB_APP_URL + '/?' + SAHIB_GETKEY_CONTROLLER + '=' + sController + '&'
	            + SAHIB_GETKEY_ACTION + '=' + sAction;
        },
        /*
		 * Devuelve una URL valida para ajax Si se ignora sController se toma
		 * por default el valor de SAHIB_CONTROLLER_NAME
		 */
        ajaxUrl: function(sController, sAction)
        {
	        if(sController != undefined && sAction == undefined)
	        {
		        sAction = sController;
		        sController = SAHIB_CONTROLLER_NAME;
	        }
	        return this.actionUrl(sController, sAction) + '&sahibAjaxMode=1&_=' + Math.random()
	            * 1000000000;
        },
        /*
		 * Devuelve TRUE si la fecha formada por d (dia), m (mes), y (año) es
		 * valida, de lo contrario devuelve FALSE
		 */
        isValidDate: function(d, m, y)
        {
	        d = parseInt(d, 10);
	        m = parseInt(m, 10);
	        y = parseInt(y, 10);
	        return !(isNaN(d) || isNaN(m) || isNaN(y)
	            || (!(y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) && m == 2 && d > 28)
	            || (d < 1 || m < 1 || y < 1000 || d > 31 || m > 12 || y > 9999)
	            || ((m == 4 || m == 6 || m == 9 || m == 11) && d > 30) || (m == 2 && d > 29));
        }
    };

// Configuración de ajax
jQuery.ajaxSetup( {
    cache: false,
    type: 'POST',
    dataType: 'json',
    error: function(Xhr, sStatusText, ExceptionThrown)
    {
	    if(Xhr.responseText.indexOf('SAHIB_AJAX_MESSAGE:') == 0)
	    {
		    alert(Xhr.responseText.replace('SAHIB_AJAX_MESSAGE:', ''));
	    }
    }
});