/*
 * Fuel UX Spinner
 * https://github.com/ExactTarget/fuelux
 *
 * Copyright (c) 2012 ExactTarget
 * Licensed under the MIT license.
 */

// SPINNER CONSTRUCTOR AND PROTOTYPE

var Spinner = function (element, options) {
	this.$element = $(element);
	this.options = $.extend({}, $.fn.spinner.defaults, options);
	this.$input = this.$element.find('.spinner-input');
	this.$element.on('keyup', this.$input, $.proxy(this.change, this));

	if (this.options.hold) {
		this.$element.on('mousedown', '.spinner-up', $.proxy(function() { this.startSpin(true); } , this));
		this.$element.on('mouseup', '.spinner-up, .spinner-down', $.proxy(this.stopSpin, this));
		this.$element.on('mouseout', '.spinner-up, .spinner-down', $.proxy(this.stopSpin, this));
		this.$element.on('mousedown', '.spinner-down', $.proxy(function() {this.startSpin(false);} , this));
	} else {
		this.$element.on('click', '.spinner-up', $.proxy(function() { this.step(true); } , this));
		this.$element.on('click', '.spinner-down', $.proxy(function() { this.step(false); }, this));
	}

	this.switches = {
		count: 1,
		enabled: true
	};

	if (this.options.speed === 'medium') {
		this.switches.speed = 300;
	} else if (this.options.speed === 'fast') {
		this.switches.speed = 100;
	} else {
		this.switches.speed = 500;
	}

	this.lastValue = null;

	this.render();

	if (this.options.disabled) {
		this.disable();
	}
};

Spinner.prototype = {
	constructor: Spinner,

	render: function () {
		var inputValue = this.$input.val();

		if (inputValue) {
			this.value(inputValue);
		} else {
			this.$input.val(this.options.value);
		}

		this.$input.attr('maxlength', (this.options.max + '').split('').length);
	},

	change: function () {
		var newVal = this.$input.val();

		if(newVal/1){
			this.options.value = newVal/1;
		}else{
			newVal = newVal.replace(/[^0-9]/g,'') || '';
			this.$input.val(newVal);
			this.options.value = newVal/1;
		}

		this.triggerChangedEvent();
	},

	stopSpin: function () {
        if(this.switches.timeout!==undefined){
            clearTimeout(this.switches.timeout);
            this.switches.count = 1;
            this.triggerChangedEvent();
        }
	},

	triggerChangedEvent: function () {
		var currentValue = this.value();
		if (currentValue === this.lastValue) return;

		this.lastValue = currentValue;

		// Primary changed event
		this.$element.trigger('changed', currentValue);

		// Undocumented, kept for backward compatibility
		this.$element.trigger('change');
	},

	startSpin: function (type) {

		if (!this.options.disabled) {
			var divisor = this.switches.count;

			if (divisor === 1) {
				this.step(type);
				divisor = 1;
			} else if (divisor < 3){
				divisor = 1.5;
			} else if (divisor < 8){
				divisor = 2.5;
			} else {
				divisor = 4;
			}

			this.switches.timeout = setTimeout($.proxy(function() {this.iterator(type);} ,this),this.switches.speed/divisor);
			this.switches.count++;
		}
	},

	iterator: function (type) {
		this.step(type);
		this.startSpin(type);
	},

	step: function (dir) {
		var curValue = this.options.value;
		var limValue = dir ? this.options.max : this.options.min;
		var digits, multiple;

		if ((dir ? curValue < limValue : curValue > limValue)) {
			var newVal = curValue + (dir ? 1 : -1) * this.options.step;

			if(this.options.step % 1 !== 0){
				digits = (this.options.step + '').split('.')[1].length;
				multiple = Math.pow(10, digits);
				newVal = Math.round(newVal * multiple) / multiple;
			}

			if (dir ? newVal > limValue : newVal < limValue) {
				this.value(limValue);
			} else {
				this.value(newVal);
			}
		} else if (this.options.cycle) {
			var cycleVal = dir ? this.options.min : this.options.max;
			this.value(cycleVal);
		}
	},

	value: function (value) {
		if (!isNaN(parseFloat(value)) && isFinite(value)) {
			value = parseFloat(value);
			this.options.value = value;
			this.$input.val(value);
			return this;
		} else {
			return this.options.value;
		}
	},

	disable: function () {
		this.options.disabled = true;
		this.$input.attr('disabled','');
		this.$element.find('button').addClass('disabled');
	},

	enable: function () {
		this.options.disabled = false;
		this.$input.removeAttr("disabled");
		this.$element.find('button').removeClass('disabled');
	}
};


// SPINNER PLUGIN DEFINITION

$.fn.spinner = function (option) {
	var args = Array.prototype.slice.call( arguments, 1 );
	var methodReturn;

	var $set = this.each(function () {
		var $this   = $( this );
		var data    = $this.data( 'spinner' );
		var options = typeof option === 'object' && option;

		if( !data ) $this.data('spinner', (data = new Spinner( this, options ) ) );
		if( typeof option === 'string' ) methodReturn = data[ option ].apply( data, args );
	});

	return ( methodReturn === undefined ) ? $set : methodReturn;
};

$.fn.spinner.defaults = {
	value: 1,
	min: 1,
	max: 999,
	step: 1,
	hold: true,
	speed: 'medium',
	disabled: false
};

$.fn.spinner.Constructor = Spinner;

$.fn.spinner.noConflict = function () {
	$.fn.spinner = old;
	return this;
};


// SPINNER DATA-API

$(function () {
	$('body').on('mousedown.spinner.data-api', '.spinner', function () {
		var $this = $(this);
		if ($this.data('spinner')) return;
		$this.spinner($this.data());
	});
});
