(function ($) {
  $.fn.ChainedSelects = function (o) {
	
	function ChainSelect(p, o) {
	  this.parent = p; 	
	  this.opts = o;  
	  this.$parent = $(this.parent);
	  
	  this.init();
	}
	ChainSelect.prototype = {
	  init: function() {
		var self = this;
		
	    $.each(
		  this.opts.selects,
		  function(i, n) {
		    self.create(i);
		  }
	    );
	  },
	  create: function(index) {
		var self = this;
		var o = this.opts.selects[index];
		var p = {};
		
		o.$el = $('#' + o.id);
		
		if (!o.$el.size() && !index) {
		  this.$parent.prepend(this.getSelect(o));	
		  
		} else if (o.$el.size() && !index) {
		  if (!$('option', o.$el).length) {
		    o.$el.parent('div').after(this.getSelect(o)).remove();
		  } else { 
			o.$el.prepend(this.getDefaultOption())//.get(0).selectedIndex = 0;
		  }
		} else { 
		  p = this.opts.selects[index - 1];
		  p.$el.parent('div').after(this.getSelect(o)); 	
		}

		o.$el = $('#' + o.id);		
		
		this.selected(o);
		this.addLoading(o);
		
		o.$el.bind(
		  'change',
		  function(e) { 
			self.change(o, index, e, true);  
		  }
		);
		this.change(o, index, {}, false);		
	  },
	  getSelect: function(o) {
		var html = '<div>' + (o.label != null ? '<label for="' + o.id + '">' + o.label + '</label>' : '') + '<select name="' + (o.name ? o.name : o.id) + '" id="' + o.id + '"' + (o.className ? (' class="' + o.className + '"') : '') + (this.opts.disableInactive ? ' disabled="disabled"' : '') + '>' + this.getDefaultOption() + '</select>' + '</div>';
		
		return $(html);
	  },
	  selected: function(o) {
		if (this.opts.resetSelect) { 
		  o.$el.get(0).selectedIndex = 0; 
		}
		if (o.selected != null) { 
		  var $o = $('option', o.$el).each(
			function(i) {
			  if($(this).val() == o.selected) {
				this.selected = true;  
			  }
			}
		  );
		}
	  },	  
	  updateSelect: function(o, a, index) {
		var i = index + 1;
		var opts = '';
		var c = true;
		if (o) {
		  opts = this.getOptions(a);
		  c = opts != '';
		  this.activate(this.display(o.$el.html(this.getDefaultOption() + opts), c), c);  
		  this.selected(o);
		  for (; i < this.opts.selects.length; i++) { 
			this.change( this.opts.selects[i], i, {}, false);
		  }
		}
	  },
	  change: function(o, index, e, loading) { 
		var v = o.$el.val();
		var n = (index < this.opts.selects.length) ? this.opts.selects[index + 1] : null;
		var i = o.$el.get(0).selectedIndex;
		var self = this;
		setTimeout(function() {
		  if (o.url) {
		    self.getJSON(o.url, index, n, v, loading);
		  } else if (n) {
		    self.updateSelect(n, n.items[i], index);  	
		  }
		}, 0);
	  },
	  addLoading: function(o) {
		o.$loading = $('<span id="' + o.$el.attr('id') + '_loading" class="' + this.opts.loadingClass + '">' + this.opts.defaultLoadingText + '</span>').hide().css('visibility', 'hidden');
		o.$el.after(o.$loading);  
	  },
	  loadingDisplay: function(o, v) {		
		(v ? o.$loading.show().css('visibility', 'visible') : o.$loading.hide().css('visibility', 'hidden'));
	  },
	  display: function($el, v) { 
		return this.opts.hideInactive ? (v ? $el.show() : $el.hide()) : $el;		
	  },
	  activate: function($el, v) { 
		return $el.attr('disabled', this.opts.disableInactive ? (v ? '' : 'disabled') : '');
	  },
	  getOptions: function(a) {
		var html = '';  
		var i = 0;		  
		if (a) {
		  for (; i < a.length; i++) {
		    var n = a[i];
			html += '<option value="' + n.value + '">' + n.text + '</option>';  
		  }
		}
		return html; 
	  },
	  getDefaultOption: function() {
		return this.opts.showDefaultOption ? ('<option value=' + this.opts.defaultOptionValue + '>' + this.opts.defaultOptionText + '</option>') : '';  
	  },
	  getJSON: function(url, index, o, value, loading) { 
		var self = this;  
		
		loading ? this.loadingDisplay(o, true) : null;
		
		$.getJSON(
		  url,
		  { value: value },
		  function(JSON) {
			o.items = JSON; 
			self.updateSelect(o, o.items, index);  
			loading ? self.loadingDisplay(o, false) : null;			
		  }
		);
	  }
	};
	  
	// handle option changes
	o = $.extend({
	  defaultOptionText: 'Please select',
	  defaultLoadingText: 'Loading...',
	  loadingClass: 'loading-select',
	  defaultOptionValue: 0,
	  showDefaultOption: true,
	  disableInactive: true,
	  hideInactive: false,	 
	  resetSelect: true,
	  selects: []
	}, o);

	// execute for each item
	return this.each(function (i) {
	  new ChainSelect(this, o);
	});
  }
}) (jQuery);