/**
 * our custom jQuery utility functions
 */

if (typeof jQuery == 'undefined') {
	throw("jquery-utils.js requires JQuery library");
}

/**
 * Initialise blockUI. You need to load jquery.blockUI.js library before using this plugin.
 * It is also automatically initialised if blockUI library is loaded when this script is run.
 */
function initialiseBlockUI() {
	jQuery.blockUI.defaults.css = {}; 
	jQuery.blockUI.defaults.overlayCSS = {}; 
	jQuery.blockUI.defaults.overlayCSS.opacity = .3; 
	jQuery.blockUI.defaults.fadeOut = 100; 
	jQuery.blockUI.defaults.message = 'Loading'; 
}

if (typeof jQuery.blockUI != 'undefined') {
	initialiseBlockUI();
}

/**
 * Global variable to store current opened dialog.
 */
var $currentOpenedDialog = null;

(function($) { 
	
	/**
	 * Show the element and then hide it within the specified interval. 
	 * Useful for showing error or information message.
	 */
	$.fn.fadeInFadeOut = function(speed) {
	    return $(this).fadeIn(speed, function() {
	    	return $(this).fadeOut(speed);
		});
	};

	/**
	 * Configure the datepicker with min and max dates.
	 */
	$.fn.configureDatepicker = function(minDateToSet, maxDateToSet) {
		if (typeof minDateToSet == 'undefined' || minDateToSet == null) {
			minDateToSet = getDateWithModifiedYear(-100);
		}
		if (typeof maxDateToSet == 'undefined' || maxDateToSet == null) {
			maxDateToSet = getDateWithModifiedYear(10);
		}
		var yearRangeToSet = minDateToSet.getFullYear() + ':' + maxDateToSet.getFullYear();
		return $(this).datepicker({
		       dateFormat: 'dd/mm/yy',
		       changeMonth: true,
		       changeYear: true,
		       minDate: minDateToSet,
		       maxDate: maxDateToSet,
		       yearRange: yearRangeToSet});
		
		function getDateWithModifiedYear(yearsToModify) {
		 	var date = new Date();
		 	date.setFullYear(date.getFullYear() + yearsToModify);
		 	return date;
		}
	};
	
	/**
	 * Display error page responseText from AJAX request.
	 */
	$.fn.displayExceptionPageFromAjaxRequest = function(ajaxResponseText) {
		var $errorPage = $(ajaxResponseText).find("#centreColumn");
		$(this).html($errorPage.html());
	};
	
	/**
	 * Hide all the elements on escape key. 
	 */
	$.fn.hideElementsOnEscapeKey = function(speed) {
		if (typeof speed == 'undefined') {
			speed = "fast";
		}
		
		var $elements = $(this);
		$(document).bind("keyup", function(e) {
			$elements.each(function(i) {       	
	          if (e.keyCode == 27 && $(this).is(":visible")) { 
	        	  $(this).slideUp(speed);
	          }
	    	});  
	    });
	};
	
	/**
	 * Hide all the elements except for the one with excludedId. 
	 */
	$.fn.hideAllElementsExcluding = function(excludedId, speed) {
		if (typeof speed == 'undefined') {
			speed = "fast";
		}
		
		$(this).each(function(i) {
			var id = $(this).attr("id");
			if((id != excludedId) && $(this).is(":visible")){
				$(this).slideUp(speed);
			}	
		});
	};
	
	/**
	 * Hide all the elements on body click. 
	 */
	$.fn.hideElementsOnBodyClick = function(speed) {
		if (typeof speed == 'undefined') {
			speed = "normal";
		}
		
		var $elements = $(this);
		$("body").bind('click', function () {
			$elements.each(function(i) {  
			    if ($(this).is(":visible")){
			    	$(this).fadeOut(speed);
			    }
			});
		});
	};
	
	/**
	 * Initialise blockUI and automatically block on AJAX request. You need to load jquery.blockUI.js library before using this plugin.
	 */
	$.fn.initialiseAjaxBlockUI = function() {
		$(this).ajaxStart(function() {
			$(this).block(); 
		});
			
		$(this).ajaxStop(function() { 
			$(this).unblock();
		});
		
		return this;
	};
	
	/**
	 * To be used for ajax json requests. 
	 */
	$.ajaxJsonRequest = function(options) {
		
		settings = $.extend(
				{
					url: "", 
					async: true,
					data: "", 
					ajaxSuccess: defaultAjaxSuccess, 
					ajaxError: defaultAjaxError
				 },
				 	options);

		$.ajax({
		    url: settings.url,
		    async: settings.async,
		    data: settings.data,
		    dataType: "json",
		    success:
		    	settings.ajaxSuccess,
		    error:
		    	settings.ajaxError
			});

		function defaultAjaxSuccess() {
			// Placeholder
		}

		function defaultAjaxError() {
			$.alertAjaxErrorMessage();
		}
	};
	
	/**
	 * Make sorting and pagination links in display tag request in AJAX.
	 * The selector is the container of the displayTag table that will be replaced by the new container.
	 */
	$.fn.ajaxifyDisplayTagLinks = function() {
		// I can't figure it out why using this reference directly throws javascript error on second AJAX request, hence the ID selector workaround
		var idSelector = this.attr("id");
		
		$(".displaytagpagelinks > a, .displaytagtable th > a").click(function() {
			$.get(this.href, {}, 
				function(data, textStatus) {
					if (textStatus == 'success') {
						$("#" + idSelector).replaceWith(data);
					} else {
						alert("Unexpected error occurred. Please try again!");
					}
				}
			);	            
			
	        return false;
	    });
		
		return this;
	};
	
	/**
	 * Populates a given dropdown list with given data and a default blank 
	 */
	$.fn.populateSelectWithDefaultBlank = function(data) {
		this.html('');
		this.append("<option value=''></option>");
		for (var i = 0; i < data.length; i++) {
			var option = data[i];
			this.append("<option value='" + option.value + "'>" + option.text + "</option>");
		}
	};

	/**
	 * Populates a given dropdown list with given data
	 */
	$.fn.populateSelect = function(data) {
		this.html('');
		for (var i = 0; i < data.length; i++) {
			var option = data[i];
			this.append("<option value='" + option.value + "'>" + option.text + "</option>");
		}
	};

	/**
	 * To be used for error callbacks in ajax json calls. 
	 */
	$.alertAjaxErrorMessage = function() {
		alert("An unexpected problem has occurred.\n\nIf the problem persists, please contact your system administrator.");
	};
	
	/**
	 * A simple jQuery plugin to limit an input characters, from http://www.unwrongest.com/projects/limit/.
	 */
    $.fn.extend({  
        limit: function(limit,element) {
			
			var interval, f;
			var self = $(this);
					
			$(this).focus(function(){
				interval = window.setInterval(substring,100);
			});
			
			$(this).blur(function(){
				clearInterval(interval);
				substring();
			});
			
			substringFunction = "function substring(){ var val = $(self).val();var length = val.length;if(length > limit){$(self).val($(self).val().substring(0,limit));}";
			if(typeof element != 'undefined')
				substringFunction += "if($(element).html() != limit-length){$(element).html((limit-length<=0)?'0':limit-length);}"
				
			substringFunction += "}";
			eval(substringFunction);
			
			substring();
       } 
    }); 
    
    /**
     * Create a cookie with the given name and value and other optional parameters.
     *
     * @example $.cookie('the_cookie', 'the_value');
     * @desc Set the value of a cookie.
     * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
     * @desc Create a cookie with all available options.
     * @example $.cookie('the_cookie', 'the_value');
     * @desc Create a session cookie.
     * @example $.cookie('the_cookie', null);
     * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
     *       used when the cookie was set.
     *
     * @param String name The name of the cookie.
     * @param String value The value of the cookie.
     * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
     * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
     *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
     *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
     *                             when the the browser exits.
     * @option String path The value of the path attribute of the cookie (default: path of page that created the cookie).
     * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
     * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
     *                        require a secure protocol (like HTTPS).
     * @type undefined
     *
     * Get the value of a cookie with the given name.
     *
     * @example $.cookie('the_cookie');
     * @desc Get the value of a cookie.
     *
     * @param String name The name of the cookie.
     * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
     * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
     *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
     *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
     *                             when the the browser exits. This is useful if you want to extend the cookie expiry date everytime its value is read.
     * @return The value of the cookie.
     * @type String
     *
     * @name $.cookie
     * @cat Plugins/Cookie
     * @author Klaus Hartl/klaus.hartl@stilbuero.de
     */
    $.cookie = function(name, value, options) {
    	options = options || {};
    	
    	// CAUTION: Needed to parenthesize options.path and options.domain
        // in the following expressions, otherwise they evaluate to undefined
        // in the packed version for some reason...
        var path = options.path ? '; path=' + (options.path) : '';
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        
        if (typeof value != 'undefined') { // name and value given, set cookie
            if (value === null) {
                value = '';
                options.expires = -1;
            }
            var expires = '';
            if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
                var date;
                if (typeof options.expires == 'number') {
                    date = getFutureDate(options.expires);
                } else {
                    date = options.expires;
                }
                expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
            }
            document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
        } else { // only name given, get cookie
            var cookieValue = null;
            if (document.cookie && document.cookie != '') {
                var cookies = document.cookie.split(';');
                for (var i = 0; i < cookies.length; i++) {
                    var cookie = $.trim(cookies[i]);
                    // Does this cookie string begin with the name we want?
                    if (cookie.substring(0, name.length + 1) == (name + '=')) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                        // renew or reset the cookie expiry date if expires option is specified
                        if (options.expires) {
                        	$.cookie(name, cookieValue, { expires: options.expires, path: path, domain: domain, secure: secure});
                        }
                        break;
                    }
                }
            }
            return cookieValue;
        }
        
        function getFutureDate(daysFromNow) {
        	var date = new Date();
            date.setTime(date.getTime() + (daysFromNow * 24 * 60 * 60 * 1000));
            return date;
        }
    };
    
    /**
     * Initialise drag and drop lists. You need to load jquery-ui.custom.min.js library before using this plugin.
     */
    $.fn.initialiseDragAndDropLists = function(options) {
    	settings = $.extend(
    	{
    		dragAndDropContainer: ".drag-and-drop-container", // Overall container
    		selectedContainer: ".selected-container", // Overall selected items container
    		availableContainer: ".available-container", // Overall available items container
    		selectedItemsContainer: ".selected-items-container", // Selected items container
    		availableItemsContainer: ".available-items-container", // Available items container
    		selectedItemsEmptyItem: ".selected-items-container .empty-item", // Empty item in selected items container
    		availableItemsEmptyItem: ".available-items-container .empty-item", // Empty item in available items container
    		selectedItem: ".selected-item", // Selected item in selected items container
    		availableItem: ".available-item", // Available item in available items container
    		connectedSortable: ".connected-sortable", // Sortable identifier
    		hiddenID: "input[name='selectedProductIDs']", // Hidden item identifier
    		droppableIndicator: "droppable-indicator", // Droppable indicator class
    		selectedItemsSortReceive: defaultSelectedItemsSortReceive, 
    		availableItemsSortReceive: defaultAvailableItemsSortReceive,
    		selectedItemsSortUpdate: defaultSelectedItemsSortUpdate,
    		availableItemsSortUpdate: defaultAvailableItemsSortUpdate
    	 },
    	 	options);

    	$(settings.selectedItemsContainer + ", " + settings.availableItemsContainer).sortable({
    		containment: $(settings.dragAndDropContainer),
    		connectWith: settings.connectedSortable,
    		items: settings.selectedItem + ", " + settings.availableItem
    	}).disableSelection();

    	$(settings.selectedItemsContainer).bind('sortstart', function(event, ui) {
    		$(settings.availableContainer).addClass(settings.droppableIndicator);
    	}).bind('sortstop', function(event, ui) {
    		$(settings.availableContainer).removeClass(settings.droppableIndicator);
    	});

    	$(settings.availableItemsContainer).bind('sortstart', function(event, ui) {
    		$(settings.selectedContainer).addClass(settings.droppableIndicator);
    	}).bind('sortstop', function(event, ui) {
    		$(settings.selectedContainer).removeClass(settings.droppableIndicator);
    	});
    	$(settings.availableItem).mouseenter(function() {
			  $("span:first",this).show();
		});
		$(settings.availableItem).mouseleave(function() {
			  $("span:first",this).hide();
		});
    	if (typeof settings.selectedItemsSortReceive == 'function') {
    		$(settings.selectedItemsContainer).bind('sortreceive', settings.selectedItemsSortReceive);
    	}

    	if (typeof settings.availableItemsSortReceive == 'function') {
    		$(settings.availableItemsContainer).bind('sortreceive', settings.availableItemsSortReceive);
    	}

    	if (typeof settings.selectedItemsSortUpdate == 'function') {
    		$(settings.selectedItemsContainer).bind('sortupdate', settings.selectedItemsSortUpdate);
    	}

    	if (typeof settings.availableItemsSortUpdate == 'function') {
    		$(settings.availableItemsContainer).bind('sortupdate', settings.availableItemsSortUpdate);
    	}

    	function defaultSelectedItemsSortReceive(event, ui) {
    		$(settings.selectedItemsEmptyItem).hide();
       		if ($(settings.availableItemsContainer).sortable('toArray').length == 0) {
       			$(settings.availableItemsEmptyItem).show();
    		}
       		$("span:first", ui.item).hide();
			ui.item.unbind('mouseenter');
    		ui.item.addClass(settings.selectedItem);
       		ui.item.removeClass(settings.availableItem);
       		ui.item.find(settings.hiddenID).removeAttr("disabled");
    	}

    	function defaultAvailableItemsSortReceive(event, ui) {
    		$(settings.availableItemsEmptyItem).hide();
    		if ($(settings.selectedItemsContainer).sortable('toArray').length == 0) {
    			$(settings.selectedItemsEmptyItem).show();
    		}
    		ui.item.mouseenter(function() {
				  $("span:first",this).show();
			});
    		ui.item.addClass(settings.selectedItem);
    		ui.item.removeClass(settings.availableItem);
       		ui.item.find(settings.hiddenID).attr("disabled", "disabled");
    	}

    	function defaultSelectedItemsSortUpdate(event, ui) {
    		// Placeholder
    	}

    	function defaultAvailableItemsSortUpdate(event, ui) {
    		// Placeholder
    	}
    };
    
    /**
     * Make an AJAX request and display the response in a jQuery dialog.
     * The dialog UI will be blocked until the AJAX responds.
     * 
     * requestUrl: the URL for the AJAX request
     * params: parameters to pass to the request
     * dialogTitle: title for the dialog
     * showDialogCloseIcon: true to show the X close button on the top right of the dialog
     * dialogCloseFunction: function to be executed when a dialog is closed
     * dialogOpenFunction: function to be executed when a dialog is opened
     */
    $.fn.makeAjaxRequestWithDialog = function(requestUrl, params, dialogTitle, showDialogCloseIcon, dialogCloseFunction, dialogOpenFunction) {
    	$dialog = $(this);
    	$dialog.block(); 
    	$.post(
    		requestUrl, 
    		params,
    		function(data) {
    			$dialog.html(data); 
    			$dialog.unblock(); 
    		}
    	);
    	
    	$dialog.dialog({ height: 550, width: 800, minHeight: 400, minWidth:700, maxHeight: 650, maxWidth: 900, 
    		title: dialogTitle, modal: true, autoOpen: false, closeOnEscape: false, zIndex: 99999,
    		close: function() {
    			$currentOpenedDialog = null;
    			
    			if (typeof dialogCloseFunction == 'function') {
    				dialogCloseFunction();
    			} 
    		},
    		open: function() {
    			$currentOpenedDialog = $dialog;
    			if (!showDialogCloseIcon) {
    				$(this).parents(".ui-dialog:first").find(".ui-dialog-titlebar-close").remove();
    			}	
    			
    			if (typeof dialogOpenFunction == 'function') {
    				dialogOpenFunction();
    			} 
    		}
    	}); 

    	$dialog.dialog('open');		
    	
    	/**
    	 * IE9 Bug Fix (the dialogue will collapse in IE9)
    	 */
    	if ( $.browser.msie && $.browser.version == '9.0') {
    		$dialog.css('minHeight', '450px');
    		$dialog.css('maxHeight', '600px');
    	} 
    	
    	return this;
    };
    
    // clear all the form input values regardless of its initial state
    $.fn.clearForm = function() {
    	return this.each(function() {
    		if (this.tagName.toLowerCase() == "form") {
    			return $(":input",this).clearForm();
    		}
    		switch (this.type) {
	    		case "password":
	    		case "textarea":
	    		case "text":
	    			this.value = "";
	    			break;
	    		case "select-multiple":
	    			this.selectedIndex = -1;
	    			break;
	    		case "select-one":
	    			this.selectedIndex = 0;
	    			break;
	    		case "radio":
	    		case "checkbox":
	    			this.checked = false;
	    			break;
    		}
    	});
    };
    
})(jQuery);
