/**
 * rSelect
 *
 * @version 1.5
 * @author Ryan Brill
 *
 * @requires prototype.js
 * @requires prototype_ss.js
 * @requires DOM.js
 * @requires EventSelectors.js
 **/

var rSelectClass = Class.create();
rSelectClass.prototype = {

	//Constructor
	initialize: function(el, s) {
		this.items = [];									// array of list items
		this.el = el;										// <select class="rSelect">
		this.label = document.createElement('div');			// the label of our dropdown
		this.labelText = document.createElement('span');	// the text node inside our label
		this.container = document.createElement('div');		// the container element
		this.ulcontainer = document.createElement('div');	// Generated UL mirroring the <select> box
		this.imask = document.createElement('iframe');		// iframe fix for IE
		this.actualCols = 0;								// Actual number of columns that get created
		this.s = {
			fixedWidth: true,					// set the width to the size of the original <select> box
			className: 'rSelectBox',			// Class name to apply to the <div> tag
			selectedClass: 'selected',			// Class name for the seleted item in the select box
			hoverClass: 'hover',				// Class name for the hover of the items in the list
			toggleClass: 'toggle',				// Class name for the toggle all items in the list
			isMultiSelect: false,				// whether or not this is a multiple select box. It will look for the attribute multiple="multiple" on the select box
			defaultLabel: 'No Values Selected',	// Default value for when no options are selected in a multi-select box
			pxPerChar: 9,						// Number of pixels to allow for each character in the label
			maxHeight: 300,						// Maximum height of the dropdown for multi selects
			maxCols: 1,							// Maximum number of columns - will be fewer colums if fewer fit in maxHeight
			pxPerRow: 22,						// Number of pixels to allow for each row
      processed: 'rSelectReplaced'        // Flag to tell whether a select has been processed yet
		};
		for (var key in s) {
			this.s[key] = s[key];
		}
		if(this.el.hasClassName(this.s.processed))
			return 0;
			
		Element.addClassName(this.el, this.s.processed);
		
		var multi = (this.el.attributes['multiple']) ? this.el.attributes['multiple'].value : this.el.getAttribute('multiple');
		if (multi && (multi.toLowerCase() == "multiple" || multi.toLowerCase() == "true")){
			this.s.isMultiSelect = true;
		}

		Element.addClassName(this.container, this.s.className);
		if(this.el.className != ''){
			Element.addClassName(this.container, this.el.className);
		}

		/* Event observers */
		Event.observe(this.ulcontainer, 'click', this.onClick.bind(this));
		Event.observe(this.ulcontainer, 'mouseover', this.onMouseOver.bind(this));
		Event.observe(this.label, 'click', this.clickLabel.bind(this));

		this.findElements();
		this.createContainers();
		Element.hide(this.el);
		this.initializeOnBlur();
		
	},
	//Methods
	findElements: function() {
		var alleles = this.el.childNodes;
		var eles = new Array();
		var totalEles = 0;
		var makeSelected = false;
		var currentRow = 0;
		var ul = document.createElement('ul');
		
		// add top level <option> and <optgroups> to eles, and count all rows (totalEles) that will get outputted
		for (var i=0; i<alleles.length; i++) {
			switch (String(alleles[i].tagName).toLowerCase()) {
				case 'optgroup':
					eles.push(alleles[i]);
					var opts = alleles[i].getElementsByTagName('option');
					if (alleles[i].label) {
						totalEles++;
					}
					totalEles = totalEles + opts.length;
				case 'option':
					if (alleles[i].childNodes.length == 1) {
						eles.push(alleles[i]);
						totalEles++;
					}
			}
		}
		
		// Check how many rows we are expecting
		var expectedCols = (Math.ceil(totalEles * this.s.pxPerRow / this.s.maxHeight) > this.s.maxCols ? this.s.maxCols : Math.ceil(totalEles * this.s.pxPerRow / this.s.maxHeight));
		
		for (var i=0; i<eles.length; i++) {
			
			// Close our current UL and start a new one
			if (currentRow > Math.ceil(totalEles / expectedCols) - 1) {
				Element.addClassName(ul, 'col');
				this.ulcontainer.appendChild(ul);
				ul = document.createElement('ul');
				currentRow = 0;
				this.actualCols++;
			}
						
			// Fix disabled options in IE
			if(eles[i].selected && eles[i].disabled){
				makeSelected = true;
			}
			if(makeSelected && !eles[i].disabled && String(eles[i].tagName).toLowerCase() == "option"){
				makeSelected = false;
				eles[i].selected = true;
			}
			
			switch (String(eles[i].tagName).toLowerCase()) {
				case 'optgroup':
					var li = document.createElement('li');
					var optgroupUl = document.createElement('ul');
					
					if (eles[i].label) {
						Element.addClassName(li, 'optlabel');
						if (eles[i].disabled) {
							Element.addClassName(li, 'disabled');
							Element.addClassName(optgroupUl, 'disabled');
						}
						li.innerHTML = eles[i].label;
						ul.appendChild(li);
						currentRow++;
					}
					
					var opts = eles[i].getElementsByTagName('option');
					currentRow = currentRow + opts.length;

					for (var j=0; j<opts.length; j++) {
						// Fix disabled options in IE
						if(opts[j].selected && (eles[i].disabled || opts[j].disabled)){
							makeSelected = true;
						}
						if(makeSelected && !eles[i].disabled && !opts[j].disabled){
							makeSelected = false;
							opts[j].selected = true;
						}
						
						this.createElement(opts[j], eles[i].disabled, optgroupUl);
					}
					ul.appendChild(optgroupUl);
					break;
				case 'option':
					if (eles[i].childNodes.length == 1) {
						this.createElement(eles[i], eles[i].disabled, ul);
						currentRow++;
					}
					break;
			}
		}
		this.actualCols++;
		if (this.actualCols > 1) {
			Element.addClassName(ul, 'col');
		}
		this.ulcontainer.appendChild(ul);
		
	},
	createElement: function(el, state, parent) {
		// Fix disabled options in IE
		var elSelected = (el.selected && (!el.disabled && (!el.parentNode || !el.parentNode.disabled))) ? true : false;
		
		var itemvar = {};
		itemvar.value = el.value;
		itemvar.el = el;
		itemvar.selected = elSelected;
		
		itemvar.disabled = (el.disabled || el.parentNode.disabled) ? true : false;
		itemvar.text = el.text;
		itemvar.li = document.createElement('li');
		if(itemvar.el.className != ''){
			Element.addClassName(itemvar.li, itemvar.el.className);
		}
		if (state == true) {
			itemvar.li.innerHTML = el.text;
			Element.addClassName(itemvar.li, 'disabled');
		}
		else {
			itemvar.a = document.createElement('a');
			itemvar.a.href = 'javascript:void(0);';
			itemvar.text = el.text;
			if(this.s.isMultiSelect){
				itemvar.a.innerHTML = '<input type="checkbox" ' + ((itemvar.selected)? ' checked="checked"' : '' ) + '> ' + el.text;
			}else{
				itemvar.a.innerHTML = el.text;
			}
			itemvar.li.appendChild(itemvar.a);
		}
		parent.appendChild(itemvar.li);
		this.items.push(itemvar);
		if (elSelected) { // create our label element
			this.setLabel(el.text);
		}
	},
	createContainers: function() {
		
		Element.addClassName(this.label, 'label');
		
		var icon = document.createElement('span');
		Element.addClassName(icon, 'icon');
		this.label.appendChild(this.labelText);
		this.label.appendChild(icon);
		
		this.container.appendChild(this.label);
		this.container.appendChild(this.ulcontainer);
		DOM.insertAfter(this.container, this.el);
		Element.addClassName(this.ulcontainer, 'ulcontainer');
		
		var dims = Element.getDimensions(this.el);
		var origDims = dims.width;
		
		var uldims = Element.getDimensions(this.ulcontainer);
		
		// container width
		if (this.s.fixedWidth == true && origDims) {
			this.container.style.width = origDims + 'px'; // set the width of the original <select>
		}
		else if (this.s.fixedWidth == true) {
			this.container.style.width = dims.width + ((this.s.isMultiSelect == true) ? 30 : 0) + ((!isNaN(this.s.maxHeight) && uldims.height > this.s.maxHeight) ? 17 : 0) + 'px'; // set the width of the container
		}
		
		var uls = this.ulcontainer.childNodes;
		var ulswidth = 0;
		for (i=0; i<uls.length; i++) {
			if (uls[i].tagName.toLowerCase() == 'ul') {
				ulswidth += uls[i].offsetWidth;
			}
		}
		
		// dropdown width
		if (this.actualCols > 1) {
			// multiple columns
			this.ulcontainer.style.width = ulswidth + ((!isNaN(this.s.maxHeight) && uldims.height > this.s.maxHeight) ? 17 : 0) + 'px'; // set the width of the UL to the width of the original <select>
			//alert ('1');
		}
		else if (ulswidth > dims.width && ulswidth.width > parseInt(this.container.style.width)) {
			
			//-------------------------
			// IE sometimes needs ulswidth and sometimes uldims.width
			//-------------------------
			
			// options longer than select
			this.ulcontainer.style.width = uldims.width + ((!isNaN(this.s.maxHeight) && uldims.height > this.s.maxHeight) ? 17 : 0) + 'px';
			//alert ('2');
		}
		else if (this.s.fixedWidth == true && origDims) {
			// set the width of the original <select>
			this.ulcontainer.style.width = origDims + 'px';
			this.ulcontainer.style.width = origDims + (origDims - this.ulcontainer.offsetWidth) + 'px';
			//alert ('3');
		}
		else {
			// normal width
			this.ulcontainer.style.width = dims.width + 'px'; // set the width of the UL container to the width of the original <select>
			this.ulcontainer.style.width = dims.width + (dims.width - this.ulcontainer.offsetWidth) + ((this.s.isMultiSelect == true) ? 30 : 0) + ((!isNaN(this.s.maxHeight) && uldims.height > this.s.maxHeight) ? 17 : 0) + 'px'; // reset the width of the UL, calculating border / padding
			//alert ('4');
		}
		
		if (!isNaN(this.s.maxHeight) && uldims.height > this.s.maxHeight) {
			this.ulcontainer.style.height = this.s.maxHeight + 'px';
			this.ulcontainer.style.overflow = 'auto';
		}
		
		var left = String(Position.cumulativeOffset(this.ulcontainer));
		left = Number(left.substring(0, left.indexOf(',')));
		left += Element.getDimensions(this.ulcontainer).width;
		if(Element.getDimensions(document.getElementsByTagName('body')[0]).width < left){
			this.ulcontainer.style.right = "0px";
		}
				
		var uldims = Element.getDimensions(this.ulcontainer);
		
		if (document.all) { 
			/* create an iFrame to fix IE bug */
			this.imask.scrolling = 'no';
			this.imask.frameborder = '0';
			this.imask.style.display = 'none';
			this.imask.style.position = 'absolute';
			this.imask.style.marginTop = '-1';
			//this.imask.style.zIndex = 10;
			this.imask.style.zIndex = -1;
			this.imask.allowTransparency = "true";
			this.ulcontainer.style.zIndex = 11;
			this.imask.style.width = uldims.width + 'px';
			this.imask.style.height = uldims.height + 'px';
			this.container.appendChild(this.imask);
			this.imask.style.top = this.ulcontainer.style.top;
			if(Element.getDimensions(document.getElementsByTagName('body')[0]).width < left){
				this.imask.style.right = "0px";
			}
		}
		this.updateLabel();
		this.hide();
	},	
	clickLabel: function(e) {
		this.toggle();
		for (var i=0; i<this.items.length; i++) {
			Element.removeClassName(this.items[i].li, this.s.hoverClass);
			Element.removeClassName(this.items[i].li, this.s.selectedClass);
			if (this.items[i].selected == true) {
				Element.addClassName(this.items[i].li, this.s.hoverClass);
				Element.addClassName(this.items[i].li, this.s.selectedClass);
			}
		}
		if (typeof(this.el.onclick) == 'function') {
			this.el.onclick();
		}
		if(!Element.visible(this.ulcontainer)){
			this.hide();
			if (typeof(this.el.onclose) == 'function') {
				this.el.onclose();
			}
			if (typeof(Event.fire) == 'function') {
				Event.fire(this.el, "close");
			}
		}
	},
	toggle: function() {
		if(Element.visible(this.ulcontainer)){
			this.hide();
		}else{
			this.show();
		}
	},
	hide: function() {
		Element.hide(this.ulcontainer);
		if (document.all) { 
			Element.hide(this.imask);
		}
	},
	show: function() {
		Element.show(this.ulcontainer);
		if (document.all) { 
			Element.show(this.imask);
		}
		var left = String(Position.cumulativeOffset(this.ulcontainer));
		left = Number(left.substring(0, left.indexOf(',')));
		left += Element.getDimensions(this.ulcontainer).width;
		if(Element.getDimensions(document.getElementsByTagName('body')[0]).width < left){
			this.ulcontainer.style.right = "0px";
		}
	},
	checkItem: function(itemvar, index){
		itemvar.selected = true;
		itemvar.el.selected = true;
		if(itemvar.li.getElementsByTagName('input')[0]){
			itemvar.li.getElementsByTagName('input')[0].checked = true;
		}
		Element.addClassName(itemvar.li, this.s.selectedClass);
		if(index){
			this.el.selectedIndex = index;
		}
	},
	uncheckItem: function(itemvar){
		itemvar.selected = false;
		itemvar.el.selected = false;
		if(itemvar.li.getElementsByTagName('input')[0]){
			itemvar.li.getElementsByTagName('input')[0].checked = false;
		}
		Element.removeClassName(itemvar.li, this.s.selectedClass);
	},
	onClick: function(e) {
		var ele = Event.element(e);
		while(ele && ele != this.container){
			if(ele.tagName.toLowerCase() == 'a'){
				break;
			}
			ele = ele.parentNode;
		}
		if(ele.tagName.toLowerCase() != 'a'){
			return;
		}
		if(this.s.isMultiSelect){
			for (var i=0; i<this.items.length; i++) {
				if (this.items[i].a == ele) {
					if(this.items[i].selected == true){
						this.uncheckItem(this.items[i]);
					}else{
						this.checkItem(this.items[i]);
					}
					break;
				}
			}
		}else{
			for (var i=0; i<this.items.length; i++) {
				if (this.items[i].a == ele) {
					this.checkItem(this.items[i], i);
				} else {
					this.uncheckItem(this.items[i]);
				}
			}
			this.hide();
		}
		var allchecked = true;
		for(var x=0; x<this.items.length; x++){
			if(this.items[x].a == ele){
				var itemvar = this.items[x];
			}
			if(this.items[x].selected == false && !Element.hasClassName(this.items[x].li, this.s.toggleClass) && !this.items[x].disabled){
				allchecked = false;
			}
		}
		if(Element.hasClassName(itemvar.li, this.s.toggleClass)){
			for(var x=0; x<this.items.length; x++){
				if(allchecked){
					//uncheck all
					this.uncheckItem(this.items[x]);
				}else{
					//check all
					if (!this.items[x].disabled) {
						this.checkItem(this.items[x]);
					}
				}
			}
		}else if(allchecked){
			for(var x=0; x<this.items.length; x++){
				if(Element.hasClassName(this.items[x].li, this.s.toggleClass)){
					//alert ('ran');
					this.checkItem(this.items[x]);
				}
			}
		}else{
			for(var x=0; x<this.items.length; x++){
				if(Element.hasClassName(this.items[x].li, this.s.toggleClass)){
					this.uncheckItem(this.items[x]);
				}
			}
		}
		this.updateLabel();
		if (typeof(this.el.onclick) == 'function') {
			this.el.onclick();
		}
		if (typeof(this.el.onchange) == 'function') {
			this.el.onchange();
		}
		if (typeof(Event.fire) == 'function') {
			Event.fire(this.el, "change");
		}
	},
	onMouseOver: function (e) {
		var ele = Event.element(e);
		if(ele.tagName.toLowerCase() != 'a' && ele.tagName.toLowerCase() != 'li'){
			return;
		}
		for (var i=0; i<this.items.length; i++) {
			Element.removeClassName(this.items[i].li, this.s.hoverClass);
			if (this.items[i].a == ele) {
				Element.addClassName(this.items[i].li, this.s.hoverClass);
			}
		}
	},
	initializeOnBlur: function() {
		Event.observe(document, 'click', this.onBlur.bind(this), false);
		Event.observe(window, 'blur', this.onBlur.bind(this), false);
	},
	onBlur: function (e) {
		var el = Event.element(e);
		var found = false;
		do {
			if (el == null || el == window || el == document.body) break;
			if(el == this.container){
				found = true;
				break;
			}
		} while(el = el.parentNode);
		if(!found && Element.visible(this.ulcontainer)){
			this.hide();
			if (typeof(this.el.onclose) == 'function') {
				this.el.onclose();
			}
			if (typeof(Event.fire) == 'function') {
				Event.fire(this.el, "close");
			}
		}
	},
	updateLabel: function() {
		var selectedItems = [];
		for(var x=0; x<this.items.length; x++){
			if(this.items[x].selected && !Element.hasClassName(this.items[x].li, this.s.toggleClass)){
				selectedItems.push(this.items[x].text);
			}
		}
		text = selectedItems.join(', ');
		if(text == ''){
			text = this.s.defaultLabel;
		}
		var dims = Element.getDimensions(this.label);
		var shorttext = text.substring(0, Math.round(dims.width/this.s.pxPerChar));
		if(shorttext != text){
			text = shorttext + '\u2026'; // truncate text and add ellipsis
		}
		this.setLabel(text);
	},
	setLabel: function(text) {
		//this.labelText.innerHTML = text; // This brakes in IE
		var text = document.createTextNode(text);
		this.labelText.innerHTML = '';
		this.labelText.insertBefore(text, this.labelText.firstChild);
	}
}

//instantiate and use the object
EventSelectors.register({
	'select.rSelect' : function(el) {
		new rSelectClass (el, {
		});
	},
	'select.rSelectNetwork' : function(el) {
		new rSelectClass (el, {
			defaultLabel: 'My Network'
		});
	},
	'select.rSelectFriends' : function(el) { 
		new rSelectClass (el, {
		   defaultLabel: 'No Friends Selected' 
		}); 
	},
	'select.rSelectContent' : function(el) {
		new rSelectClass (el, {
			defaultLabel: 'No Content Selected'
		});
	}
}, true);
