/**
 * QuantityField.js - Fancy increment/decrement controls for quantity inputs
 * 
 * @version 1.0
 * @author Maurice Snip <mauricesnip at hotmail dot com>
 * @uses jQuery 1.4.2 <http://jquery.com/>
 * 
 * @param {Mixed} input jQuery selector or reference to an HTMLInputElement object
 * @param {Object} options Optional parameters
 */
function QuantityField(input, options) {
	this.input = $(input);
	this.options = options;
	this.currentValue = 0;
	this.updateTimeout = null;
	this.keyUpTimeout = null;
	this.construct();
};

QuantityField.prototype = {
	/**
	 * @constructor
	 */
	construct: function() {
		if(this.input.length) {
			var self = this;
			
			this.options = $.extend({
				min: 0,
				max: 0,
				step: 1,
				smartMax: false,
				updateDelay: 0,
				onUpdate: null
			}, this.options || {});
			
			if(this.options.smartMax && this.input.attr('maxlength') != -1) {
				this.options.max = Math.pow(10, parseInt(this.input.attr('maxlength'))) - 1;
			}
			
			this.input.keyup(function(e) {
				switch(e.keyCode) {
					case 38:
						self.increment();
						break;
					case 40:
						self.decrement();
						break;
					default:
						self.keyUpHandler();
						break;
				}
			});
			
			this.input.val(this.options.min);
			this.currentValue = this.input.val();
			this.createControls();
		}
	},
	
	/**
	 * Create necessary nodes/controls
	 */
	createControls: function() {
		var self = this;
		
		var inputWrapper = document.createElement('div');
		inputWrapper.className = 'quantity';
		
		var incrementButton = document.createElement('span');
		incrementButton.className = 'increment';
		incrementButton.innerHTML = 'Increment';
		
		var decrementButton = document.createElement('span');
		decrementButton.className = 'decrement';
		decrementButton.innerHTML = 'Decrement';
		
		$(incrementButton).attr('unselectable', 'on').css('-moz-user-select', 'none').bind('selectstart', function() {
			return false;
		}).click(function() {
			self.increment();
		});
		
		$(decrementButton).attr('unselectable', 'on').css('-moz-user-select', 'none').bind('selectstart', function() {
			return false;
		}).click(function() {
			self.decrement();
		});
		
		/*@cc_on
		$(incrementButton).dblclick(function() {
			self.increment();
		});
		
		$(decrementButton).dblclick(function() {
			self.decrement();
		});
		@*/
		
		this.input.wrap(inputWrapper).after(decrementButton).after(incrementButton);
	},
	
	/**
	 * Check if the given value is in the min/max range
	 * 
	 * @param {Number} val The value to check
	 * @return {Boolean} inRange Whether or not the value is in range
	 */
	inMinMaxRange: function(val) {
		var inRange = false;
		
		if(this.options.max > 0) {
			inRange = (val >= this.options.min && val <= this.options.max);
		}
		else {
			inRange = (val >= this.options.min);
		}
		
		return inRange;
	},
	
	/**
	 * When the keyUp event is fired
	 */
	keyUpHandler: function() {
		this.clearTimeout(this.keyUpTimeout);
		
		var self = this;
		var timeoutDelay = 0;
		var floatVal = parseFloat(this.input.val());
		
		if(this.input.val()) {
			if(floatVal < this.options.min) {
				timeoutDelay = 3000;
			}
			
			this.keyUpTimeout = setTimeout(function(){
				if(!isNaN(floatVal) && /^\d+$/.test(self.input.val()) && self.inMinMaxRange(floatVal)) {
					self.update();
				}
				else {
					self.input.val(self.currentValue);
				}
			}, timeoutDelay);
		}
	},
	
	/**
	 * Increment the value with given step
	 */
	increment: function() {
		var floatVal = parseFloat(this.input.val());
		
		if(this.inMinMaxRange(floatVal + this.options.step)) {
			this.input.val(floatVal += this.options.step);
			this.update();
		}
	},
	
	/**
	 * Decrement the value with given step
	 */
	decrement: function() {
		var floatVal = parseFloat(this.input.val());
		
		if(this.inMinMaxRange(floatVal - this.options.step)) {
			this.input.val(floatVal -= this.options.step);
			this.update();
		}
	},
	
	/**
	 * Update properies and call onUpdate callback
	 */
	update: function() {
		this.clearTimeout(this.updateTimeout);
		
		var self = this;
		this.currentValue = this.input.val();
		
		if(this.options.onUpdate) {
			if(this.options.updateDelay > 0) {
				this.updateTimeout = setTimeout(function() {
					self.options.onUpdate(self.input.get(0));
				}, this.options.updateDelay);
			}
			else {
				this.options.onUpdate(this.input);
			}
		}
	},
	
	/**
	 * Clear a timeout
	 */
	clearTimeout: function(timeout) {
		if(timeout) {
			clearTimeout(timeout);
			timeout = null;
		}
	}
};
