$.fn.addToCartAjax = function(options) {
	// Compose the options of the functions
	var _options = jQuery.extend({
		// Callback that provides with the product ID:
		productIdCallback           : function() {},
		// Callback that updates the price information
		// (handled internally by default)
		quantityUpdateCallback         : null,
		// Where AJAX requests are routed (base href)
		ajaxHrefBase                : '',
		// Where product details are fetched from (added to ajax base href)
		ajaxHrefProductDetails      : 'product',
		// Where price information is fetched from (added to ajax base href)
		ajaxHrefPriceInfo           : 'price',
		// Where add-to-cart request is sent (added to ajax base href)
		ajaxHrefAddToCart           : 'add-to-cart',
		// Loading class that is added to links that are clicked
		ajaxLoadingClass            : 'add-to-cart-loading',
		// Popup window dimensions
		windowWidth                 : 500,
		windowHeight                : 500,
		// Private properties
		// (but may be changed, if required)
		_windowElementId            : 'pqa',
		_selectorPricePreview       : '.pricePreview',
		_selectorTotalPricePreview  : '.totalPricePreview',
		_selectorWindowLoading      : '.add-to-cart-win-loading', // Shown when loading stuff
		_selectorQuantityField      : 'input[@name^=quantity]', // We listen to update, to update price info
		_selectorSubmitButton       : '.add-to-cart-button'
	}, options);
	
	// Remember about matched objects in the page:
	_matchedObjects = this;
	
	// Listen to the click event on each of the matched elements:
	_matchedObjects.click(function($eventObject){
		// We show the loading state in the clicked link:
		_setLoadingStateInClickedLink($(this));
		
		// We retrieve the product ID:
		var productId = _options.productIdCallback($eventObject, $(this));
		
		// Get the ajax url, for a product request:
		var ajaxObj = _getProductAjaxUrl(productId);
		
		// Request product details, via AJAX
		$.ajax({
			url      : ajaxObj.url,
			data     : ajaxObj.data,
			dataType : ajaxObj.dataType,
			// When the request has completed:
			success  : function(data, textStatus) {
				// Open the window
				_openWindow($("view", data).text());
				
				// Now, 
				// - we listen to updates of the quantity field:
				// - set the value of the quantity field to 1
				// - trigger an update (to fetch price info)
				$('#' + _options._windowElementId + ' ' + _options._selectorQuantityField).unbind().bind('keyup', {
					productID  : productId
				}, (_options.quantityUpdateCallback ? _options.quantityUpdateCallback : _onQuantityUpdate)).val('1').trigger('keyup');
				
				// Also, we listen to the click event of the "Add to cart" button
				$('#' + _options._windowElementId + ' ' + _options._selectorSubmitButton).unbind().bind('click', {
					productID : productId
				}, _onQuantitySubmit);
			
				// We remove the loading state from the clicked link
				_unsetLoadingStateInClickedLink();
			}
		});
		
		// We return false, to override default behavior of the link
		return false;
	});
	
	// Get product url and variables
	function _getProductAjaxUrl(productId) {
		return {
			url : _options.ajaxHrefBase + _options.ajaxHrefProductDetails,
			data: { 
				id : productId
			},
			dataType: "xml"
		};
	}
	
	// Get price url and variables
	function _getPriceAjaxUrl(productId, quantityNumber) {
		return {
			url : _options.ajaxHrefBase + _options.ajaxHrefPriceInfo,
			data: { 
				id       : productId,
				quantity : quantityNumber
			},
			dataType: "xml"
		};
	}
	
	// Get add-to-cart url and variables
	function _getAddToCartAjaxUrl(productId, quantityNumber) {
		return {
			url : _options.ajaxHrefBase + _options.ajaxHrefAddToCart,
			data: { 
				id       : productId,
				quantity : quantityNumber
			},
			dataType: "xml"
		};
	}
	
	// Add loading state to clicked link
	function _setLoadingStateInClickedLink($elm) {
		$elm.removeClass('add-to-cart').addClass(_options.ajaxLoadingClass);
	}
	
	// Remove loading state from clicked link
	function _unsetLoadingStateInClickedLink() {
		$('.' + _options.ajaxLoadingClass).removeClass(_options.ajaxLoadingClass).addClass('add-to-cart');
	}
	
	// Create the window
	function _openWindow(content) {
		// Close currently opened window
		_closeWindow();
		
		// Create the DIV that contains the window content
		var op = '<div id="'+ _options._windowElementId +'">' + content + '</div>';
			
		// Append the window to the page:
		$('body').append(op);
		
		// Show the dialog, with the view render inside:
		$('#' + _options._windowElementId).dialog({
			modal       : true,
			resizable   : false,
			draggable   : false,
			width       : _options.windowWidth,
			height      : _options.windowHeight,
			dialogClass : 'flora' // Apply jquery-ui skin
		});
		
		// Overlay fix:
		$('.ui-dialog-overlay').css({
			backgroundColor : '#000000',
			opacity : 0.8
		});
		
		// return the window:
		return $('#' + _options._windowElementId);
	}
	
	// Add/Remove loading state to popup window
	function _setLoadingStateInWindow(flag) {
		var selector = '#' + _options._windowElementId + ' ' + _options._selectorWindowLoading;
		$(selector).stop();
		if(flag) {
			$(selector).fadeIn();
		} else {
			$(selector).fadeOut(200);
		}
	}
	
	// Close the window
	function _closeWindow() {
		$('#' + _options._windowElementId).remove();
	}
	
	// Respond to update of quantity field
	function _onQuantityUpdate($eventObject) {
		// If a numeric key has been typed:
		var key = $eventObject.charCode ? $eventObject.charCode : $eventObject.keyCode ? $eventObject.keyCode : 0;
		//if(key == 0 || key == 8 || (key > 47 && key < 58)) {
			// Show loading state in window:
			_setLoadingStateInWindow(true);
			
			// Get the ajax url, for a price-info request:
			var ajaxObj = _getPriceAjaxUrl($eventObject.data.productID, $(this).val());
			
			// Fetch price information
			$.ajax({
				url      : ajaxObj.url,
				data     : ajaxObj.data,
				dataType : ajaxObj.dataType,
				// When request has completed successfully:
				success  : function(data, textStatus) {
					if(textStatus == 'success') {
						// Show updated price information
						_showUpdatedPrice($('price', data).text(), $('totalPrice', data).text());
						
						// Remove loading state from window:
						_setLoadingStateInWindow(false);
					}
				}
			});
		//}
	}
	
	// Show updated price
	function _showUpdatedPrice(price, totalPrice) {
		$('#' + _options._windowElementId + ' ' + _options._selectorPricePreview).html(price);
		$('#' + _options._windowElementId + ' ' + _options._selectorTotalPricePreview).html(totalPrice);
	}
	
	// Respond to a click on the "Add to cart" button in the popup window
	function _onQuantitySubmit($eventObject) {
		// Show loading state:
		_setLoadingStateInWindow(true);
			
		// Get the ajax url, for an add-to-cart request:
		var ajaxObj = _getAddToCartAjaxUrl($eventObject.data.productID, $('#' + _options._windowElementId + ' ' + _options._selectorQuantityField).val());
		
		// Add to cart:
		$.ajax({
			url       : ajaxObj.url,
			data      : ajaxObj.data,
			dataType  : ajaxObj.dataType,
			// When the request has been fully served, we close the window:
			success   : function(data, textStatus) {
				if(textStatus == 'success') {
					// Close the window
					_closeWindow();
				}
			}
		});
		
		// Override default behavior of the button
		return false;
	}
}