(function ($) {

	/**
	Read the maxlength parameter of a textarea and limit character entry to this value
	**/
	$.fn.textarea = function (options) {

		options = $.extend({
			maxlength: null,
			text: '<p class="counter"><span class="left"/>&nbsp;characters remaining</p>',
			left: 'span.left',
			used: 'span.used',
			counter: function ($c) { $(this).parent().append($c); }
		}, options);

		// get length of current text selection
		function getSelectedLength(el) {
			if (el.selectionEnd) { return el.selectionEnd - el.selectionStart; }
			else {
				var selection = document.selection && document.selection.createRange();
				if (selection.text) { return selection.text.length; }
			}
			return 0;
		}

		return $(this).each(function () {

			var $this = $(this),
				$counter = $(options.text),
				$left = $counter.find(options.left),
				$used = $counter.find(options.used),
				maxlength = parseInt($this.attr('maxlength'), 10) || options.maxlength,
				update = function () {
					if ($this.val().length > maxlength) {
						$this.val($this.val().substr(0, maxlength));
					}
					$left.text(maxlength - $this.val().length);
					$used.text($this.val().length);
				},
				interval;

			if (maxlength > 0) {
				options.counter.call(this, $counter);
				$this.keypress(function (e) {
					// allow ctrl-x && backspace
					if (!e.ctrlKey && e.which != 8) {
						// allow cursor keys and other non-characters
						if (e.charCode || e.which == 13 || e.which < 37 || e.which > 40) {
							// allow if still spare characters
							if ($this.val().length >= maxlength) {
								// allow if replacing highlighted text
								if (getSelectedLength(e.target) == 0) {
									e.preventDefault();
								}
							}
						}
					}
					setTimeout(update);
				})
				// IE and webkit don't support keypress on all keys so bind a counter to keydown and remove on keyup
				.keydown(function (e) {
					if (!e.charCode && e.keyCode == 8) {
						update();
						clearInterval(interval);
						interval = setInterval(update, 100);
					}
				})
				.keyup(function () {
					update();
					clearInterval(interval);
				});
				update();
			}

		});

	}

	$.fn.radiogroup = function (options) {

		options = $.extend({
			selected: 'selected'
		}, options);

		return $(this).each(function () {

			var $this = $(this),
				$options = $this.find('div.option');

			$options.click(function (e, data) {
				$this.find('div.option').removeClass(options.selected);
				$(this).addClass(options.selected);
				// prevent infinite loop from forcing click event on radio button
				if (data && data.stopEvent) {
					return;
				}
				$(this).find('input[type="radio"]').trigger('click', { stopEvent: true });
			}).css('cursor', 'pointer');

			if (!Modernizr.flexbox) {
				var hs = $options.map(function () { return $(this).height() }).get();
				$options.height(Math.max.apply(Math, hs));
			}

			$this.find('div.option input:checked').click();

		});

	}

	/**
	Bind events onto a group of inputs/selects to trigger when event has fired on all elements
	**/
	$.fn.dateChange = function (callback, opts) {

		if (typeof callback == 'undefined') {
			$(this).trigger('dateChange.change');
			return $(this);
		}
		if (typeof callback == 'string') {
			$(this).trigger('dateChange.' + callback);
			return $(this);
		}

		opts = $.extend({
			inputs: 'input, select',
			event: 'change'
		}, opts);

		return $(this).each(function () {

			var $this = $(this),
				$inputs = $this.find(opts.inputs),
				touched = [];

			$inputs.each(function (i) {
				if ($(this).val() != '') {
					touched[i] = '1';
				}
				$(this).bind(opts.event, function () {
					touched[i] = '1';
					update();
				})
			});

			function update() {
				if (touched.join('').length == $inputs.length) {
					$this.trigger('dateChange.change');
				}
			}

			$this.bind('dateChange.reset', function () { touched = []; });
			$this.bind('dateChange.change', function () { callback.call($this, $inputs.map(function () { return $(this).val(); }).get()); });

		});

	}

})(jQuery);

