/*
    RateIt
    version 0.99.2
    03/07/2011
    http://rateit.codeplex.com
    Twitter: @gjunge

*/
(function ($) {
	$.fn.rateit = function (p1, p2) {
		//quick way out.
		var options = {}; var mode = 'init';
		if (this.length == 0) return this;


		var tp1 = $.type(p1);
		if (tp1 == 'object' || p1 === undefined || p1 == null) {
			options = $.extend({}, $.fn.rateit.defaults, p1); //wants to init new rateit plugin(s).
		}
		else if (tp1 == 'string' && p2 === undefined) {
			return this.data('rateit-' + p1); //wants to get a value.
		}
		else if (tp1 == 'string') {
			mode = 'setvalue'
		}

		return this.each(function () {
			var item = $(this);

			//shorten all the item.data('rateit-XXX'), will save space in closure compiler, will be like item.data('XXX') will become x('XXX')
			var itemdata = function (k, v) {
				k = 'rateit-' + k;

				if (arguments.length === 1)
					return item.data(k);

				item.data(k, v);
				return v;
			};

			//add the rate it class.
			if (!item.hasClass('rateit')) item.addClass('rateit');

			var ltr = item.css('direction') != 'rtl';

			// set value mode
			if (mode == 'setvalue') {
				if (!itemdata('init')) throw 'Can\'t set value before init';


				//if readonly now and it wasn't readonly, remove the eventhandlers.
				if (p1 == 'readonly' && !itemdata('readonly')) {
					item.find('.rateit-range').unbind();
					itemdata('wired', false);
				}

				if (itemdata('backingfld')) {
					//if we have a backing field, check which fields we should update. 
					//In case of input[type=range], although we did read its attributes even in browsers that don't support it (using fld.attr())
					//we only update it in browser that support it (&& fld[0].min only works in supporting browsers), not only does it save us from checking if it is range input type, it also is unnecessary.
					var fld = $(itemdata('backingfld'));
					if (p1 == 'value') fld.val(p2);
					if (p1 == 'min' && fld[0].min) fld[0].min = p2;
					if (p1 == 'max' && fld[0].max) fld[0].max = p2;
					if (p1 == 'step' && fld[0].step) fld[0].step = p2;
				}

				itemdata(p1, p2);
			}

			//init rateit plugin
			if (!itemdata('init')) {

				//get our values, either from the data-* html5 attribute or from the options.
				itemdata('min', itemdata('min') || options.min);
				itemdata('max', itemdata('max') || options.max);
				itemdata('step', itemdata('step') || options.step);
				itemdata('readonly', itemdata('readonly') !== undefined ? itemdata('readonly') : options.readonly);
				itemdata('resetable', itemdata('resetable') !== undefined ? itemdata('resetable') : options.resetable);
				itemdata('backingfld', itemdata('backingfld') || options.backingfld);
				itemdata('starwidth', itemdata('starwidth') || options.starwidth);
				itemdata('starheight', itemdata('starheight') || options.starheight);
				itemdata('value', itemdata('value') || options.min);
				//are we LTR or RTL?

				if (itemdata('backingfld')) {
					//if we have a backing field, hide it, and get its value, and override defaults if range.
					var fld = $(itemdata('backingfld'));
					itemdata('value', fld.hide().val());

					if (fld[0].nodeName == 'INPUT') {
						if (fld[0].type == 'range' || fld[0].type == 'text') { //in browsers not support the range type, it defaults to text

							itemdata('min', parseInt(fld.attr('min')) || itemdata('min')); //if we would have done fld[0].min it wouldn't have worked in browsers not supporting the range type.
							itemdata('max', parseInt(fld.attr('max')) || itemdata('max'));
							itemdata('step', parseInt(fld.attr('step')) || itemdata('step'));
						}
					}
					if (fld[0].nodeName == 'SELECT' && fld[0].options.length > 1) {
						itemdata('min', Number(fld[0].options[0].value));
						itemdata('max', Number(fld[0].options[fld[0].length - 1].value));
						itemdata('step', Number(fld[0].options[1].value) - Number(fld[0].options[0].value));
					}
				}

				//Create the necessart tags.
				item.append('<div class="rateit-reset"></div><div class="rateit-range"><div class="rateit-selected" style="height:' + itemdata('starheight') + 'px"></div><div class="rateit-hover" style="height:' + itemdata('starheight') + 'px"></div></div>');

				//if we are in RTL mode, we have to change the float of the "reset button"
				if (!ltr) {
					item.find('.rateit-reset').css('float', 'right');
					item.find('.rateit-selected').addClass('rateit-selected-rtl');
					item.find('.rateit-hover').addClass('rateit-hover-rtl');
				}
				itemdata('init', true);
			}


			//set the range element to fit all the stars.
			var range = item.find('.rateit-range');
			range.width(itemdata('starwidth') * (itemdata('max') - itemdata('min'))).height(itemdata('starheight'));


			//set the value if we have it.
			if (itemdata('value')) {
				var score = (itemdata('value') - itemdata('min')) * itemdata('starwidth');
				item.find('.rateit-selected').width(score);
			}

			var resetbtn = item.find('.rateit-reset');

			var calcRawScore = function (element, event) {
				var pageX = (event.changedTouches) ? event.changedTouches[0].pageX : event.pageX;

				var offsetx = pageX - $(element).offset().left;
				if (!ltr) offsetx = range.width() - offsetx;
				if (offsetx > range.width()) offsetx = range.width();
				if (offsetx < 0) offsetx = 0;

				return score = Math.ceil(offsetx / itemdata('starwidth') * (1 / itemdata('step')));
			};


			//

			if (!itemdata('readonly')) {
				//if we are not read only, add all the events

				//if we have a reset button, set the event handler.
				if (itemdata('resetable')) {
					resetbtn.click(function () {
						itemdata('value', itemdata('min'));
						range.find('.rateit-hover').hide().width(0);
						range.find('.rateit-selected').width(0).show();
						if (itemdata('backingfld')) $(itemdata('backingfld')).val(itemdata('min'));
						item.trigger('reset');
					});

				}
				else {
					resetbtn.hide();
				}



				//when the mouse goes over the range div, we set the "hover" stars.
				if (!itemdata('wired')) {
					range.bind('touchmove touchend', touchHandler); //bind touch events
					range.mousemove(function (e) {
						var score = calcRawScore(this, e);
						var w = score * itemdata('starwidth') * itemdata('step');
						var h = range.find('.rateit-hover');
						if (h.data('width') != w) {
							range.find('.rateit-selected').hide();
							h.width(w).show().data('width', w);
							item.trigger('hover', [(score * itemdata('step')) + itemdata('min')]);
						}
					});
					//when the mouse leaves the range, we have to hide the hover stars, and show the current value.
					range.mouseleave(function (e) {
						range.find('.rateit-hover').hide().width(0).data('width', '');
						item.trigger('hover', [null]);
						range.find('.rateit-selected').show();
					});
					//when we click on the range, we have to set the value, hide the hover.
					range.mouseup(function (e) {
						var score = calcRawScore(this, e);

						var newvalue = (score * itemdata('step')) + itemdata('min');
						itemdata('value', newvalue);
						if (itemdata('backingfld')) {
							$(itemdata('backingfld')).val(newvalue);
						}
						range.find('.rateit-hover').hide();
						range.find('.rateit-selected').width(score * itemdata('starwidth') * itemdata('step')).show();
						item.trigger('hover', [null]).trigger('rated', [newvalue]);
					});

					itemdata('wired', true);
				}
				if (itemdata('resetable')) {
					resetbtn.show();
				}
			}
			else {
				resetbtn.hide();
			}
		});
	};

	//touch converter http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript/
	function touchHandler(event) {

		var touches = event.originalEvent.changedTouches,
                first = touches[0],
                type = "";
		switch (event.type) {
			case "touchmove": type = "mousemove"; break;
			case "touchend": type = "mouseup"; break;
			default: return;
		}

		var simulatedEvent = document.createEvent("MouseEvent");
		simulatedEvent.initMouseEvent(type, true, true, window, 1,
                              first.screenX, first.screenY,
                              first.clientX, first.clientY, false,
                              false, false, false, 0/*left*/, null);

		first.target.dispatchEvent(simulatedEvent);
		event.preventDefault();
	};


	//some default values.
	$.fn.rateit.defaults = { min: 0, max: 5, step: 0.5, starwidth: 16, starheight: 16, readonly: false, resetable: true };
	eds3_5_jq(document).ready(function ($) {
		//invoke it on all div.rateit elements. This could be removed if not wanted.
		$('div.rateit').rateit();
	});
})(eds3_5_jq);