var bIsFirebugReady = (!!window.console && !!window.console.log);

	$(document).ready(
		function (){
			// update the plug-in version
			$("#idPluginVersion").text($.Calculation.version);

			
			// bind the recalc function to the quantity fields
			$("input[name^=qty_item_]").bind("keyup", recalc);
			// run the calculation function now
			recalc();

			// automatically update the "#totalSum" field every time
			// the values are changes via the keyup event
			$("input[name^=sum]").sum("keyup", "#totalSum");
			
			// automatically update the "#totalAvg" field every time
			// the values are changes via the keyup event
			$("input[name^=avg]").avg({
				bind:"keyup"
				, selector: "#totalAvg"
				// if an invalid character is found, change the background color
				, onParseError: function(){
					this.css("backgroundColor", "#cc0000")
				}
				// if the error has been cleared, reset the bgcolor
				, onParseClear: function (){
					this.css("backgroundColor", "");
				}
			});

			// automatically update the "#minNumber" field every time
			// the values are changes via the keyup event
			$("input[name^=min]").min("keyup", "#numberMin");

			// automatically update the "#minNumber" field every time
			// the values are changes via the keyup event
			$("input[name^=max]").max("keyup", {
				selector: "#numberMax"
				, oncalc: function (value, options){
					// you can use this to format the value
					$(options.selector).val(value);
				}
			});

			// this calculates the sum for some text nodes
			$("#idTotalTextSum").click(
				function (){
					// get the sum of the elements
					var sum = $(".textSum").sum();

					// update the total
					$("#totalTextSum").text(sum.toString() + " лв.");
				}
			);

			// this calculates the average for some text nodes
			$("#idTotalTextAvg").click(
				function (){
					// get the average of the elements
					var avg = $(".textAvg").avg();

					// update the total
					$("#totalTextAvg").text(avg.toString());
				}
			);
			
			
			/*inputs = document.getElementsByTagName("input");
			for(var b = 0; b < inputs.length; b++) {
				if(inputs[b].type == "text" && inputs[b].className == "input_qty") {
					inputs[b].parentNode.nextSibling.firstChild.checked = true;
					inputs[b].parentNode.nextSibling.firstChild.style.backgroundPosition = "0 -19px";
					inputs[b].parentNode.parentNode.style.backgroundColor = "#e5f3fa";
				}
			}*/
			
			
			/*$("#qty_item_1").keyup(
				function (){
					
					this.parentNode.nextSibling.firstChild.checked = true;
					this.parentNode.nextSibling.firstChild.style.backgroundPosition = "0 -19px";
					this.parentNode.parentNode.style.backgroundColor = "#e5f3fa";
					
				}
			);*/
		}
	);
	
	function recalc(){
		$("[id^=total_item]").calc(
			// the equation to use for the calculation
			"qty * price",
			// define the variables used in the equation, these can be a jQuery object
			{
				qty: $("input[name^=qty_item_]"),
				price: $("[id^=price_item_]")
			},
			// define the formatting callback, the results of the calculation are passed to this function
			function (s){
				// return the number as a dollar amount
				return s.toFixed(2) + " лв.";
			},
			// define the finish callback, this runs after the calculation has been complete
			function ($this){
				// sum the total of the $("[id^=total_item]") selector
				var sum = $this.sum();
				
				$("#grandTotal").text(
					// round the results to 2 digits
					sum.toFixed(2) + " лв."
				);
				
				var total_sum = document.createElement("input");
				total_sum.type = "hidden";
				total_sum.name = "total_sum";
				total_sum.id = "total_sum";
				total_sum.value = sum;
				
				var element_total_sum = document.getElementById("total_sum");
				
				if (element_total_sum != null) {
					var old_total_sum = document.getElementById("total_sum");
					document.getElementById("order_form").removeChild(old_total_sum);
				}
				
				var order_form = document.getElementById("order_form");
				
				if (order_form != null) {
					document.getElementById("order_form").appendChild(total_sum);
				}
				
			}
		);
	}


(function($){

	// set the defaults
	var defaults = {
		// regular expression used to detect numbers, if you want to force the field to contain
		// numbers, you can add a ^ to the beginning or $ to the end of the regex to force the
		// the regex to match the entire string: /^(-|-\$)?(\d+(,\d{3})*(\.\d{1,})?|\.\d{1,})$/g
		reNumbers: /(-|-\$)?(\d+(,\d{3})*(\.\d{1,})?|\.\d{1,})/g
		// this function is used in the parseNumber() to cleanse up any found numbers
		// the function is intended to remove extra information found in a number such
		// as extra commas and dollar signs. override this function to strip European values
		, cleanseNumber: function (v){
			// cleanse the number one more time to remove extra data (like commas and dollar signs)
			// use this for European numbers: v.replace(/[^0-9,\-]/g, "").replace(/,/g, ".")
			return v.replace(/[^0-9.\-]/g, "");
		}
		// should the Field plug-in be used for getting values of :input elements?
		, useFieldPlugin: (!!$.fn.getValue)
		// a callback function to run when an parsing error occurs
		, onParseError: null
		// a callback function to run once a parsing error has cleared
		, onParseClear: null
	};
	
	// set default options
	$.Calculation = {
		version: "0.4.07",
		setDefaults: function(options){
			$.extend(defaults, options);
		}
	};


	$.fn.parseNumber = function(options){
		var aValues = [];
		options = $.extend(options, defaults);
		
		this.each(
			function (){
				var
					// get a pointer to the current element
					$el = $(this),
					// determine what method to get it's value
					sMethod = ($el.is(":input") ? (defaults.useFieldPlugin ? "getValue" : "val") : "text"),
					// parse the string and get the first number we find
					v = $.trim($el[sMethod]()).match(defaults.reNumbers, "");
					
				// if the value is null, use 0
				if( v == null ){
					v = 0; // update value
					// if there's a error callback, execute it
					if( jQuery.isFunction(options.onParseError) ) options.onParseError.apply($el, [sMethod]);
					$.data($el[0], "calcParseError", true);
				// otherwise we take the number we found and remove any commas
				} else {
					// clense the number one more time to remove extra data (like commas and dollar signs)
					v = options.cleanseNumber.apply(this, [v[0]]);
					// if there's a clear callback, execute it
					if( $.data($el[0], "calcParseError") && jQuery.isFunction(options.onParseClear) ){
						options.onParseClear.apply($el, [sMethod]);
						// clear the error flag
						$.data($el[0], "calcParseError", false);
					} 
				}
				aValues.push(parseFloat(v, 10));
			}
		);

		// return an array of values
		return aValues;
	};


	$.fn.calc = function(expr, vars, cbFormat, cbDone){
		var
			// create a pointer to the jQuery object
			$this = this
			// the value determine from the expression
			, exprValue = ""
			// track the precision to use
			, precision = 0
			// a pointer to the current jQuery element
			, $el
			// store an altered copy of the vars
			, parsedVars = {}
			// temp variable
			, tmp
			// the current method to use for updating the value
			, sMethod
			// a hash to store the local variables
			, _
			// track whether an error occured in the calculation
			, bIsError = false;

		// look for any jQuery objects and parse the results into numbers			
		for( var k in vars ){
			// replace the keys in the expression
			expr = expr.replace( (new RegExp("(" + k + ")", "g")), "_.$1");
			if( !!vars[k] && !!vars[k].jquery ){
				parsedVars[k] = vars[k].parseNumber();
			} else {
				parsedVars[k] = vars[k];
			}
		}
		
		this.each(
			function (i, el){
				var p, len;
				// get a pointer to the current element
				$el = $(this);
				// determine what method to get it's value
				sMethod = ($el.is(":input") ? (defaults.useFieldPlugin ? "setValue" : "val") : "text");

				// initialize the hash vars
				_ = {};
				for( var k in parsedVars ){
					if( typeof parsedVars[k] == "number" ){
						_[k] = parsedVars[k];
					} else if( typeof parsedVars[k] == "string" ){
						_[k] = parseFloat(parsedVars[k], 10);
					} else if( !!parsedVars[k] && (parsedVars[k] instanceof Array) ) {
						// if the length of the array is the same as number of objects in the jQuery
						// object we're attaching to, use the matching array value, otherwise use the
						// value from the first array item
						tmp = (parsedVars[k].length == $this.length) ? i : 0;
						_[k] = parsedVars[k][tmp];
					}

					// if we're not a number, make it 0
					if( isNaN(_[k]) ) _[k] = 0;

					// check for decimals and check the precision
					p = _[k].toString().match(/\.\d+$/gi);
					len = (p) ? p[0].length-1 : 0;

					// track the highest level of precision
					if( len > precision ) precision = len; 
				}


				// try the calculation
				try {
					exprValue = eval( expr );
					
					// fix any the precision errors
					if( precision ) exprValue = Number(exprValue.toFixed(Math.max(precision, 4)));

					// if there's a format callback, call it now
					if( jQuery.isFunction(cbFormat) ){
						// get return value
						var tmp = cbFormat.apply(this, [exprValue])
						// if we have a returned value (it's null null) use it
						if( !!tmp ) exprValue = tmp;
					}
		
				// if there's an error, capture the error output
				} catch(e){
					exprValue = e;
					bIsError = true;
				}
				
				// update the value
				$el[sMethod](exprValue.toString());
			}
		);
		
		// if there's a format callback, call it now
		if( jQuery.isFunction(cbDone) ) cbDone.apply(this, [this]);

		return this;
	};


	$.each(["sum", "avg", "min", "max"], function (i, method){
		$.fn[method] = function (bind, selector){
			// if no arguments, then return the result of the aggregate function
			if( arguments.length == 0 )
				return math[method](this.parseNumber());

			// if the selector is an options object, get the options
			var bSelOpt = selector && (selector.constructor == Object) && !(selector instanceof jQuery);

			// configure the options for this method
			var opt = bind && bind.constructor == Object ? bind : {
				  bind: bind || "keyup"
				, selector: (!bSelOpt) ? selector : null
				, oncalc: null
			};
			
			// if the selector is an options object, extend	the options
			if( bSelOpt ) opt = jQuery.extend(opt, selector);
	
			// if the selector exists, make sure it's a jQuery object
			if( !!opt.selector ) opt.selector = $(opt.selector);
			
			var self = this
				, sMethod
				, doCalc = function (){
					// preform the aggregate function
					var value = math[method](self.parseNumber(opt));
					// check to make sure we have a selector				
					if( !!opt.selector ){
						// determine how to set the value for the selector
						sMethod = (opt.selector.is(":input") ? (defaults.useFieldPlugin ? "setValue" : "val") : "text");
						// update the value
						opt.selector[sMethod](value.toString());
					}
					// if there's a callback, run it now
					if( jQuery.isFunction(opt.oncalc) ) opt.oncalc.apply(self, [value, opt]);
				};
			
			// perform the aggregate function now, to ensure init values are updated
			doCalc();
			
			// bind the doCalc function to run each time a key is pressed
			return self.bind(opt.bind, doCalc);
		}
	});
	
	/*
	 * Mathmatical functions
	 */
	var math = {
		// sum an array
		sum: function (a){
			var total = 0, precision = 0;
			
			// loop through the value and total them
			$.each(a, function (i, v){
				// check for decimals and check the precision
				var p = v.toString().match(/\.\d+$/gi), len = (p) ? p[0].length-1 : 0;
				// track the highest level of precision
				if( len > precision ) precision = len; 
				// we add 0 to the value to ensure we get a numberic value
				total += v;
			});

			// fix any the precision errors
			if( precision ) total = Number(total.toFixed(precision));
	
			// return the values as a comma-delimited string
			return total;
		},
		// average an array
		avg: function (a){
			// return the values as a comma-delimited string
			return math.sum(a)/a.length;
		},
		// lowest number in array
		min: function (a){
			return Math.min.apply(Math, a);
		},
		// highest number in array
		max: function (a){
			return Math.max.apply(Math, a);
		}
	};
	

})(jQuery);

