/**
 * PollWidget
 * @module pollwidget
 * @requires yahoo, dom, event, element, profiler, yuiloader, pollmodel
 */

/*
 * Searches the index of 'obj' in 'container'.
 * 'container' can be an array, or any collection
 */
var indexOf = function(container, obj){
    var result = -1;
    for (var i = 0; i < container.length; i++) {
        if (container[i] == obj) {
            result = i;
            break;
        }
    };
    return result;
}

/*
 * Searches the index of 'date' in 'array'.
 * 'array' must be an array with dates
 */
var indexOfDate = function(array, date){
    var result = -1;
    for (var i = 0; i < array.length; i++) {
        if (array[i].toString("MM/dd/yyyy") == date.toString("MM/dd/yyyy")) {
            result = i;
            break;
        }
    };
    return result;
}

/*
 * Converts a string to a pref number.
 * "Yes" = 1
 * "No" = 3
 * Any other value converted to 2.
 */
var stringToPref = function(choice){
    if (choice == resource.label.yes) 
        return 1;
    if (choice == resource.label.no) 
        return 3;
    return 2;
};

/*
 * Converts a pref number to string using locale dependent Yes/No/Maybe strings.
 * 1 = "Yes"
 * 3 = "No"
 * Any other value converted to "Maybe".
 */
var prefToString = function(pref){
    if (pref == 1) 
        return resource.label.yes;
    if (pref == 3) 
        return resource.label.no;
    return resource.label.maybe;
}

/*
 * Converts a pref number to CSS string. Does not use locale dependent values
 * 1 = "Yes"
 * 3 = "No"
 * Any other value converted to "Maybe".
 */
var prefToCssString = function(pref){
    if (pref == 1) 
        return "Yes";
    if (pref == 3) 
        return "No";
    return "Maybe";
}

/*
 * Trick for creating tags with IE and FF
 * IE needs to specify the full XML code of the tag being created
 * FF needs only the tagname (and throw exception on the first)
 */
var compatibleCreate = function(full, tagName){
    try {
        return document.createElement(full);
    } 
    catch (e) {
        return document.createElement(tagName);
    }
}

/*
 * Escapes the text as 'safe' HTML string
 */
escapeHTML = function(s) {
	var div = document.createElement('div');
	var text = document.createTextNode(s);
	div.appendChild(text);
	return div.innerHTML;
}

var choice_date_weekdays = null
var choice_date_months = null
/*
 * 
 */
choice_date = function(date) {
	if (choice_date_weekdays == null) {
		choice_date_weekdays = resource.datetime.weekdays.sunday.split(" ")
	}
	if (choice_date_months == null) {
		choice_date_months = resource.datetime.months.split(" ");
	}
	var month = "&lt;invalid_month&gt;";
	var weekday = "&lt;invalid_weekday&gt;";
	if (choice_date_months.length == 12) {
		month = choice_date_months[date.getMonth()];
	}
	if (choice_date_weekdays.length == 7) {
		weekday = choice_date_weekdays[date.getDay()];
	}
	return resource.format.choiceDate.replace("%W", weekday).replace("%M", month).replace("%D", new Number(date.getDate()).toString()).replace("%Y", new Number(date.getFullYear()).toString());
}

var Spinner = function(el) {

	var spinner = document.createElement("div");
    spinner.className = "spinner";
    el.appendChild(spinner);

    this.start = function() {
    	YAHOO.util.Dom.addClass(spinner,"busy");
    }
    
    this.stop = function() {
    	YAHOO.util.Dom.removeClass(spinner,"busy");
    }
}



/**
 * A common panel widget.<br>
 * It differs from YAHOO.widget.Panel
 * <ul>
 * 		<li>It can't be positioned</li>
 * 		<li>It can have any number of Widgets in his Items property</li>
 * 		<li>One can assign event handlers through the Panel's 'listeners' property</li>
 * </ul>
 * @class Panel
 * @constructor
 * @param {Object} containerId
 * @param {Object} config
 * @extends YAHOO.widget.Module 
 */
var Panel = function(containerId, config){
    /* 
     * Call the constructor at the top, so we can
     * define Event handlers trough the listeners property for
     * the inherited events too.
     */
    Panel.superclass.constructor.call(this, containerId);
    
    var that = this;
    config = config ||
    {};
    
    if (!this.items) {
		/**
		 * @property items
		 * @type Array[] (Component[])
		 */
        this.items = config.items || [];
    }
    
    if (!this.listeners) {
		/**
		 * @property listeners
		 * @type Object
		 */
        this.listeners = config.listeners ||
        {};
    }
    
    
    
    /**
	 * 
	 */
    this.render = function(){
    	
    	
        /*
         * Rendering the Panel itself, then
         * rendering its items.
         */
        Panel.superclass.render.apply(that, arguments);
        for (var item in that.items) {
            that.items[item].render();
        }
        
        /*
         * Firing the itemsRendered event.
         */
        that.itemsRendered.fire();
    };


    
    /*
     * Assigning of the Event handlers.
     */
    for (var listener in this.listeners) {
        this[listener].subscribe(this.listeners[listener], this);
    }
    
    /*
     * Events
     */
	/**
	 * @event itemsRendered
	 */
    this.itemsRendered = new YAHOO.util.CustomEvent("itemsRendered", that);
};
YAHOO.extend(Panel, YAHOO.widget.Module);

/**
 * A popup widget, for voting through the Calendar.<br>
 * <strong>Singleton.</strong>
 * @class VoterPopup
 * @constructor
 * @param {Object} container
 * @param {Object} cellId
 * @extends Panel 
 */

var VoterPopup = function(container, cellId){
    var that = this;
    
    /**
     * One of the 3 option is clicked.
     * @param {Object} e
     */
    this.inputClicked = function(e){
        var eObj = e || window.event;
        
        var target = eObj.currentTarget || eObj.toElement || eObj.srcElement;
        /*
         * Firing the onClick Event
         */        
        that.onClick.fire(that.cellId, target.className);
    };
    
    /*
     * Generating the markup.
     */
	/**
     * @property element
     * @type HTMLDivElement
     */
    this.element = document.createElement('div');
    this.element.className = "voterPopup";
    container.appendChild(this.element);
    var table = document.createElement('table');
    table.className = "voterPopupTable"
    var tRow = table.insertRow(0);
    var tCell = tRow.insertCell(0);
    tCell.className = "yesCell";
    var input = compatibleCreate('<input name="popupRadio"></input>', 'input');
    input.type = "radio";
    input.name = "popupRadio";
    input.className = "Yes";
    input.onclick = this.inputClicked;
    tCell.appendChild(input);
    
    tCell = tRow.insertCell(1);
    tCell.className = "maybeCell";
    input = compatibleCreate('<input name="popupRadio"></input>', 'input');
    input.type = "radio";
    input.name = "popupRadio";
    input.className = "Maybe";
    input.onclick = that.inputClicked;
    tCell.appendChild(input);
    
    tCell = tRow.insertCell(2);
    tCell.className = "noCell";
    input = compatibleCreate('<input name="popupRadio"></input>', 'input');
    input.type = "radio";
    input.className = "No";
    input.name = "popupRadio";
    input.onclick = that.inputClicked;
    tCell.appendChild(input);
    
    this.element.appendChild(table);
    /*
     * End markup generating.
     */
	/**
     * @property cellId
     * @type String cell Id
     */
    this.cellId = cellId;
    
    /*
     * Showing, hiding and destroying the popup.
     */
	/**
	 * 
	 */
    this.show = function(){
        this.element.style.display = "block";
    }
	/**
	 * 
	 */
    this.hide = function(){
        this.element.style.display = "none";
    }
	/**
	 * 
	 */
    this.destroy = function(){
        container.removeChild(that.element);
    }
    
    /* Events */
	/**
	 * @event onClick
	 */
    this.onClick = new YAHOO.util.CustomEvent("onClick", that);
    
}


/**
 * Choices widget
 * @class Choices
 * @extends Panel
 * @constructor
 * @param {Object} containerId
 * @param {Object} config
 */
var Choices = function(containerId, config){
    var that = this;
    config = config ||
    {};
    
    this.dirty = false;
    
    /* Private variables */
    var list;
    var user = config.userModel;
    var data;
    var tableMarkup;
    
	currentFocus=null;

    setCurrentFocus = function(val) {
    	currentFocus = val;
    }
    
	var Cell = function(choiceRow, config) {
		var tCell = choiceRow.getRow().insertCell(choiceRow.getCellCount());
		Cell.superclass.constructor.apply(this,[ tCell,config ]);
	}
	YAHOO.extend(Cell,YAHOO.util.Element);
	
	var TextCell = function(choiceRow,config) {
		this.spawner = null;
		var tcell = this;
		var spawned = false;
		
		TextCell.superclass.constructor.apply(this,[choiceRow,config]);
		var input = document.createElement("input");
		
		input.value = config.text; 
		input.onkeyup = function(e) {
	        var eObj = e || window.event;	        
	        var target = eObj.currentTarget || eObj.fromElement;
			data.getChoiceById(config.optionId).text = e.target.value;
			if(!spawned && tcell.spawner && e.target.value) {
				setCurrentFocus(config.optionId);
				spawned = true;
				that.addTextChoiceEvent.fire(config.parentId, true);
			}
		}
		
		input.onfocus = function(e) {
	        var eObj = e || window.event;	        
	        var target = eObj.currentTarget || eObj.fromElement;
	        if(config.otheroptions && target.value==resource.label.otheroptions) {
	        	target.value="";
	        }
		}
		
		input.onblur = function(e) {
	        var eObj = e || window.event;	        
	        var target = eObj.currentTarget || eObj.fromElement;
	        if(config.otheroptions && target.value=="") {
	        	target.value=resource.label.otheroptions;
	        }
		}
				
		this.appendChild(input);
		if(currentFocus == config.optionId) {
			input.focus();
		}
		
	}
	YAHOO.extend(TextCell,Cell);
	
	var AddTextCell = function(choiceRow,config) {
		AddTextCell.superclass.constructor.apply(this,[choiceRow,config]);
		
		this.set("innerHTML",'<img src="/assets/img/add.png">');
		this.get("element").className="addCell";
		this.get("element").onclick=function() {
			currentFocus = null;
			that.addTextChoiceEvent.fire(config.optionId);
		}
		
	}
	YAHOO.extend(AddTextCell,Cell);
	
	var DateCell = function(choiceRow, config) {
	    DateCell.superclass.constructor.apply(this, [choiceRow, config]);
		
	    this.addClass("choicesCell");
	    this.addClass("dateCell");
	    this.get("element").innerHTML=config.innerHTML;
	};
	YAHOO.extend(DateCell, Cell);
	
	var DeleteCell = function(choiceRow, config) {
	    DeleteCell.superclass.constructor.apply(this, [choiceRow, config]);	    
		this.get("element").innerHTML='<img src="/assets/img/delete.png">';
		this.get("element").className="deleteCell";
		if(config.date !== undefined && config.date){			
			this.get("element").title=Date.parse(config.date).toString("MM/dd/yyyy");
		}
		this.get("element").onclick=function() {
			currentFocus = null;
			that.deleteChoiceEvent.fire(config.optionId);
		};
	};
	YAHOO.extend(DeleteCell, Cell);
	
	var RadioCell = function(choiceRow,config) {
		RadioCell.superclass.constructor.call(this,choiceRow,config);
        var div = document.createElement('div');
		var input = compatibleCreate('<input type="radio"></input>', "input");
		
        this.addClass("choicesCell");
        this.addClass("inputCell");
        //= 'choicesCell inputCell';
        try {
	        this.appendChild(div);            	
        }
        catch (e) {
        }
        input.name = config.name;
        input.type = config.type;
        input.className = config.className;
        input.style.display = config.display;
        input.title = config.title;
        input.value = config.value;
        input.onclick = config.onclick;
        div.name = config.name;
        div.appendChild(input);
        
        this.getRadio = function() {
        	return input;
        };
        
        this.getDiv = function() {
        	return div;
        };
	}
	YAHOO.extend(RadioCell, Cell);
	
	var StatisticsCell = function(choiceRow,config) {
	    StatisticsCell.superclass.constructor.apply(this, [choiceRow, config]);
        var choiceId = config.choiceId;
        this.className = 'choicesCell statisticsCell';  
        this.renderStatistics=function() {
	        var statisticsHTML = '<table class="choiceStatistic">';       
	        statisticsHTML += '<tr clazz="statistic-row">';
	        
	        var yesPct = 0
	        try {
	            yesPct = data.statistics[choiceId].yes * 100 / data.statistics[choiceId].total;
	        } 
	        catch (e) {
	        }
	        
	        var maybePct = 0
	        try {
	            maybePct = data.statistics[choiceId].maybe * 100 / data.statistics[choiceId].total;
	        } 
	        catch (e) {
	        }
	        
	        var noPct = 0
	        try {
	            noPct = (data.statistics[choiceId].total -
	            data.statistics[choiceId].yes -
	            data.statistics[choiceId].maybe) *
	            100 /
	            data.statistics[choiceId].total;
	        } 
	        catch (e) {
	        }
	        if (!yesPct && !maybePct && !noPct) {
	            maybePct = 100;
	        }
	        statisticsHTML += '<td class="statisticYes" width="' + yesPct + '%"' +
	        (yesPct == 0 ? ' style="display: none;"' : '') +
	        '><div>&nbsp;</div></td>';
	        statisticsHTML += '<td class="statisticMaybe" width="' + maybePct + '%"' +
	        (maybePct == 0 ? ' style="display: none;"' : '') +
	        '><div>&nbsp;</div></td>';
	        statisticsHTML += '<td class="statisticNo" width="' + noPct + '%"' +
	        (noPct == 0 ? ' style="display: none;"' : '') +
	        '><div>&nbsp;</div></td>';
	        
	        statisticsHTML += '</tr></table>';
	        this.get("element").innerHTML = statisticsHTML;        	
        }
        
        this.renderStatistics();
	}
	YAHOO.extend(StatisticsCell, Cell);
	
	var ChoiceRow = function(table, config) {

		var that = this;		
		var tRow = table.insertRow(table.rows.length);
	    ChoiceRow.superclass.constructor.apply(this, [tRow, config]);
	    if(config.alternate) {
	    	tRow.className+=" alter";
	    }
	    if(config.last) {
	    	tRow.className+=" last";
	    }
	    tRow.optionId = config.optionId;
	    tRow.onmouseover = function() {
	    	that.mouseOverEvent.fire(config.optionId);
	    }

  	    tRow.onmouseout = function() {
	    	that.mouseOutEvent.fire(config.optionId);
	    }

	    
	    if(config.date) {
	    	tRow.title=config.date.toString("MM/dd/yyyy");
	    }

		
		this.getRow = function() {
			return tRow;
		}	
		
		this.getCellCount = function() {
			return tRow.cells.length;
		}
		
		this.getCells = function() {
			return tRow.cells;	
		}
		
		this.getCell = function(i) {
			return tRow.cells.item(i);
		}
		
		this.mouseOverEvent = new YAHOO.util.CustomEvent("mouseOverEvent", that);
		this.mouseOutEvent = new YAHOO.util.CustomEvent("mouseOutEvent", that);
		
	}
	YAHOO.extend(ChoiceRow, YAHOO.util.Element);
	
	var CreateChoiceRow = function(table, config) {
		var subRow;
		var i;
	    CreateChoiceRow.superclass.constructor.apply(this, [table, config]);
	    if(config.text !== null) {
	    	var textConfig = {
	    		text:config.text,
	    		optionId:config.optionId,
	    		parentId:config.parentId || null,
	    		className:(config.subrow ? "subrow" :"") 
	    	};
	    	if(config.subrow) {
	    		textConfig.colSpan=2;
	    	}
	    	else {
	    		textConfig.otheroptions = true;
	    	}
	    	var textCell = new TextCell(this,textConfig);
	    	if(config.last) {
	    		textCell.spawner = true;
	    	}
	    	if(!config.subrow) {
			    var addTextCell = new AddTextCell(this,{
			    	optionId:config.optionId
			    });	    	
	    	}
	    }
	    else {
		    var dateCell = new DateCell(this,{
		    	innerHTML:choice_date(Date.parse(config.date)),
		    	optionId:config.optionId,
		    	value:config.value
		    });
		    var addTextCell = new AddTextCell(this,{
		    	optionId:config.optionId
		    });	    	
	    }
	    var deleteCell = new DeleteCell(this,{
	    	date:config.date,
	    	optionId:config.optionId,
	    	value:config.value
	    });
	    if(config.free && config.free.length>0) {
	    	for(i=0;i<config.free.length; i++) {
	    		subConfig = {};
	    		subConfig.optionId = config.free[i].optionId;
	    		subConfig.text = config.free[i].text;
	    		subConfig.date = config.free[i].date;
	    		subConfig.subrow=true;
    			subConfig.last=(i==config.free.length-1);
    			subConfig.alternate = config.alternate;
    			subConfig.parentId = config.optionId;
	    		subRow = that.addRow( subConfig, CreateChoiceRow, null);
	    	}
	    }	    
	};
	YAHOO.extend(CreateChoiceRow,ChoiceRow);
    
	var CloseChoiceRow = function(table, config) {
	    CloseChoiceRow.superclass.constructor.apply(this, [table, config]);
	    var rconfig = {
	    	date:config.date,
	    	optionId:config.optionId,
	    	name:"result",
	    	type:"radio",
	    	className:"Maybe",
	    	display:"none",
	    	title:resource.label.maybe,
	    	value:config.optionId,
	    	onclick:that.resultClick
	    };
	    var closeCell = new RadioCell(this,rconfig);
       	closeCell.addClass("maybeCellInact");
       	closeCell.addClass("first");
   	    var div = closeCell.getDiv(); 
        div.title = resource.label.maybe;
        div.onclick = function(e) {
	        data.setResult(config.optionId);        	
        }
        
        var dateConfig = {
	    	optionId:config.optionId,
	    	value:config.value
        };
        dateConfig.innerHTML = config.text || choice_date(Date.parse(config.date));
        
        
	    var dateCell = new DateCell(this,dateConfig);	 
	    
	    var statisticsCell = new StatisticsCell(this,{
	    	date:config.date,
	    	optionId:config.optionId,
	    	value:config.value,
	    	choiceId:config.optionId
	    });	    
	    
	    var resultChanged = function(type,args) {
	    	var oldResult=args[0];
	    	var currentResult=args[1];
	    	if(config.optionId == currentResult) {
				closeCell.getRadio().checked = true;
				closeCell.addClass("maybeCell");
				closeCell.removeClass("maybeCellInact");	    		
	    	} else if(config.optionId == oldResult) {
	    		closeCell.getRadio().checked = false;
				closeCell.removeClass("maybeCell");
				closeCell.addClass("maybeCellInact");	    		
	    	}	    	
	    }
	    
	    data.onResultModified.subscribe(resultChanged,that)
	}
    YAHOO.extend(CloseChoiceRow,ChoiceRow);
    
    var VoteChoiceRow = function(table, config) {
	    VoteChoiceRow.superclass.constructor.apply(this, [table, config]);
	    var div;
	    var choiceId = config.optionId;
	    var rconfig = {
	    	date:config.date,
	    	optionId:config.optionId,
	    	value:config.value,
	    	choiceId:config.optionId,
	    	name:config.optionId,
	    	type:"radio",
	    	display:"none",
	    	onclick:that.choiceClick
	    };
   		rconfig.value = data.getVoteByVoterChoice(config.optionId, config.tempname ? "tempId" : YAHOO.util.Cookie.get("pId"));
	    
	    var Config = function(c) {
	    	var i;
	    	for(i in c) {
	    		if(c.hasOwnProperty(i)) {
	    			this[i]=c[i];
	    		}
	    	}
	    }
	    Config.prototype = rconfig;
        var savedPref = rconfig.value;     
	    var yesCell = new RadioCell(this,new Config({
	    	className:"Yes",
	    	title:resource.label.yes
	    }));
	    div = yesCell.getDiv(); 
        div.title = resource.label.yes;
        div.onclick = that.choiceClick;            
        if (savedPref == 1) {
            yesCell.getRadio().checked = true;                
            yesCell.getRadio().defaultChecked = true;
            yesCell.addClass("yesCell");
        }
        else {
        	yesCell.addClass("yesCellInact");
        }
        yesCell.addClass("first");

	    var maybeCell = new RadioCell(this,new Config({
	    	className:"Maybe",
	    	title:resource.label.maybe
	    }));
	    div = maybeCell.getDiv(); 
        div.title = resource.label.maybe;
        div.onclick = that.choiceClick;            
        if (savedPref == 2) {
            maybeCell.getRadio().checked = true;                
            maybeCell.getRadio().defaultChecked = true;
            maybeCell.addClass("maybeCell");
        }
        else {
        	maybeCell.addClass("maybeCellInact");
        }

	    var noCell = new RadioCell(this,new Config({
	    	className:"No",
	    	title:resource.label.no	    	
	    }));
	    div = noCell.getDiv(); 
        div.title = resource.label.no;
        div.onclick = that.choiceClick;            
        if (savedPref == 3) {
            noCell.getRadio().checked = true;                
            noCell.getRadio().defaultChecked = true;
            noCell.addClass("noCell");
        }
        else {
        	noCell.addClass("noCellInact");
        }        	

        var dateConfig = {
	    	optionId:config.optionId,
	    	value:config.value
        };
        dateConfig.innerHTML = config.text || choice_date(Date.parse(config.date));
        
        
	    var dateCell = new DateCell(this,dateConfig);	 
	    
	    var statisticsCell = new StatisticsCell(this,{
	    	date:config.date,
	    	optionId:config.optionId,
	    	value:config.value,
	    	choiceId:choiceId
	    });
	    	    
	    var voteChanged = function(eventType, args){
	        var optionId = args[0].toString();
	        var voterId = args[1];
	        var pref = args[2];
        	if(choiceId == optionId) {
        		yesCell.removeClass("yesCell");
        		yesCell.addClass("yesCellInact");
        		maybeCell.removeClass("maybeCell");
        		maybeCell.addClass("maybeCellInact");
        		noCell.removeClass("noCell");
        		noCell.addClass("noCellInact");
        		switch(Number(pref)) {
        			case 1:
        				yesCell.getRadio().checked = true;
        				yesCell.addClass("yesCell");
        				yesCell.removeClass("yesCellInact");
        				break;
        			case 2:
        				maybeCell.getRadio().checked = true;
        				maybeCell.addClass("maybeCell");
        				maybeCell.removeClass("maybeCellInact");
        				break;
        			case 3:
        				noCell.getRadio().checked = true;
        				noCell.addClass("noCell");
        				noCell.removeClass("noCellInact");
        				break;
        		}
        		statisticsCell.renderStatistics();
        	}
	    }
        data.onVoteModified.subscribe(voteChanged, that);
	}
    YAHOO.extend(VoteChoiceRow,ChoiceRow);
    
    var GroupChoiceRow = function(table,config,rowC) {
    	var i;
    	var headingConfig;
    	var headingCell;
    	var choice;
    	var choiceConfig;
    	GroupChoiceRow.superclass.constructor.apply(this, [table, config]);
		headingConfig = {
			innerHTML:(config.text) ? '<span style="font-weight:bold">'+config.text+'</span>' : choice_date(Date.parse(config.date)),
			colSpan:5,
			className:"groupheader"
		};    		

    	headingCell = new Cell(this,headingConfig);
    	    	
    	if(!config.free || config.free.length==0) {
    		choice = that.addRow({
    			date:config.date,
    			text:" ",
    			optionId:config.optionId,
    			value:config.value,
    			alternate:config.alternate,
    			last:true
    		},rowC,null);
    	}
    	else {
    		for(i=0;i<config.free.length;i++) {
    			choiceConfig = config.free[i];
	    		choice = that.addRow({
	    			date:config.date,
	    			optionId:choiceConfig.optionId,
	    			value:choiceConfig.value,
	    			text:choiceConfig.text,
	    			alternate:config.alternate,
	    			last:(i==config.free.length-1)
	    		},rowC,null);    		
    		}
    	}    	
    }
    YAHOO.extend(GroupChoiceRow,ChoiceRow);
    
    
    
    /* Creating markup */
	/**
	 * @property header
	 * @type HTMLDivElement
	 */
    this.header = document.createElement('div');
    this.header.className = 'header';
    var headerText = document.createElement('span');
    headerText.className = 'headerText';
    headerText.innerHTML = resource.label.choices;
    this.header.appendChild(headerText);
    
    if(config.allowDelete) {
	    var but = document.createElement("div");
	    but.innerHTML='add other choices <img src="/assets/img/add.png">';
	    but.className="addButton";
	    but.onclick = function() {
	    	for(var i=0; i<data.Choices.length; i++) {
	    		if(data.Choices[i].text !== undefined) {
	    			return false;
	    		}
	    	}
	    	
	    	that.addFreeChoiceEvent.fire();
	    }
	    this.header.appendChild(but);
    }

    this.spinner = new Spinner(this.header);
    
	/**
	 * @property body
	 * @type HTMLTableElement
	 */    
    this.body = document.createElement('div');
    this.body.id = containerId + '-container';
    this.body.className = 'container';
    
    tableMarkup = document.createElement('table');
    tableMarkup.id = containerId + '-list'
    
    this.body.appendChild(tableMarkup);
    
    if (config.dataModel && config.dataModel.Result) {
        config.renderHeader = false;
    }
    
    if (config.closing === true) {
        config.renderHeader = false;
    }
    
    /**
     * When the choice list is empty, use this function for
     * rendering information.
     * This function can be set by the
     * 		renderEmptyInfo
     * config option.
     * @property renderEmptyInfo
     * @type function
     */
    this.renderEmptyInfo = config.renderEmptyInfo ||
    function(){
        var tRow = tableMarkup.insertRow(0);
        tCell = tRow.insertCell(0);
        tCell.className = "emptyInfo";
        tCell.innerHTML = '<img src="/assets/img/create_add_sign.gif">';
        tCell = tRow.insertCell(1);
        tCell.className = "emptyInfo";
        tCell.innerHTML = resource.instruction.clickToAdd;
        return tRow;
    };
    
    
    /* If header must be rendered */
    if (config.renderHeader !== false) {
        var tRow = tableMarkup.insertRow(0);
        tRow.className = "alter";
        var tCell = tRow.insertCell(0);
        tCell.className = 'choicesHeader';
        tCell.innerHTML = '<img src="/assets/img/vote_head_yes.png">';
        tCell.className = 'choicesHeader yesHeader';
        tCell = tRow.insertCell(1);
        tCell.className = 'choicesHeader naHeader';
        tCell.innerHTML = '<img src="/assets/img/vote_head_na.png">';
        tCell = tRow.insertCell(2);
        tCell.className = 'choicesHeader noHeader';
        tCell.innerHTML = '<img src="/assets/img/vote_head_no.png">';
        tCell = tRow.insertCell(3);
        tCell.className = 'choicesHeader';
        tCell = tRow.insertCell(4);
        tCell.className = 'choicesHeader';    	
    }
    
    this.renderEmptyInfo();
    
    /* Onmouseover function, for highlighting rows */
    var choicesOver = function(type, args, obj){
		
    	YAHOO.util.Dom.addClass(obj, "highlightedRow");
        that.onMouseOver.fire(args[0]);
    }
    
    /* Onmouseout function, for highlighting rows */
	/**
	 * @method choicesOut
	 * @param {Object} e
	 * @private
	 */
    var choicesOut = function(type, args, obj){
        
        YAHOO.util.Dom.removeClass(obj, "highlightedRow");
        that.onMouseOut.fire();
    }
    
    this.addTextChoice = function(e) {
        var eObj = e || window.event;
        
        var target = eObj.currentTarget || eObj.toElement;
        data.addTextChoiceByDateStr(target.title,"");               
    }
    
	/**
	 * 
	 * @param {Object} e
	 */
    this.removeChoice = function(e){
        var eObj = e || window.event;
        
        var target = eObj.currentTarget || eObj.toElement;
        data.removeChoiceByDateStr(target.title);               
    };
    
	/**
	 * Event handler for the choice onclick
	 * @param {Object} e
	 */
    this.choiceClick = function(e){
        var eObj = e || window.event;
        var target = eObj.currentTarget || eObj.fromElement || eObj.srcElement;
        var userId = config.tempname ? "tempId" : YAHOO.util.Cookie.get("pId");
        
        var parent = target.parentNode;
        while (parent.tagName != "TR") {
            parent = parent.parentNode;
        }
                
        var pref = stringToPref(target.title);
        
        data.setVote(target.name, pref, userId);
    };
    
	/**
	 * Event handler for the result onclick (when in closing state)
	 * @param {Object} e
	 */
    this.resultClick = function(e){
    };
    
	/**
	 * Adding a new choice
	 * @param {Object} dateString
	 * @param {Object} index
	 */
    this.addRow = function(rowConfig,c,sc){
    	var tRow = new c(tableMarkup,rowConfig,sc);
    	tRow.mouseOverEvent.subscribe(choicesOver,tRow.getRow());
    	tRow.mouseOutEvent.subscribe(choicesOut,tRow.getRow());
        return tRow;
    }
    
	/**
	 * 
	 * @param {Object} index
	 */
    this.deleteRow = function(index){
        tableMarkup.deleteRow(index);        
    };
    
	/**
	 * 
	 */
    this.clear = function(){
        var isHeader = config.renderHeader !== false ? 1 : 0;
        while (tableMarkup.rows.length > isHeader) {
            that.deleteRow(isHeader);
        }
    };
    
	/**
	 * 
	 */
    this.setDates = function(){
    	var fc;
    	var i,o;
    	var rowConstructor;
    	var sRowConstructor = null;
    	var tr;
    	var rowConfig;
        that.clear();
        if (!data.Choices.length) {
            that.renderEmptyInfo();
            return true;
        }

		if (config.closing === true) {
        	rowConstructor = CloseChoiceRow;
        	sRowConstructor = CloseChoiceRow;
        }
        else {
        	rowConstructor = VoteChoiceRow;
        	sRowConstructor = VoteChoiceRow;
        }
        
        fc = data.Choices;
        for ( i in fc ) {
        	if(fc.hasOwnProperty(i)) {
        		choice = fc[i];
        		if(!choice.text && choice.free && choice.free.length>0) {
        			rowConstructor = GroupChoiceRow;        			
        			tableMarkup.className="padded";
        			break;
        		}
        	}
        }
		
		if(config.allowDelete) {
        	rowConstructor = CreateChoiceRow;
        } 
        
        if(!currentFocus) {
        	setCurrentFocus(data.lastIndex);
        }
        
        for ( i=0;i<data.Choices.length;i++ ) {
    		choice = fc[i];
			rowConfig = {
    			date:choice.date,
    			optionId:choice.optionId,
    			value:choice.value,
    			text:choice.text || null,
    			free:choice.free || [],
    			alternate:false
			}
			if(i % 2==1) {
				rowConfig.alternate=true;
			}
			if(rowConfig.text && !config.allowDelete) {
            	that.addRow(rowConfig,GroupChoiceRow,sRowConstructor);        		
			}
			else {
        		that.addRow(rowConfig,rowConstructor,sRowConstructor);        		
				
			}
        }
    };
    
	/**
	 * 
	 * @param {Object} dataModel
	 */
    this.setModel = function(dataModel){
        data = dataModel;
        data.onDataLoaded.subscribe(that.initData, that);
        data.onVoteModified.subscribe(that.voteChanged, that);
        data.onChoiceRemoved.subscribe(that.setDates, that);
        data.onChoicesSet.subscribe(that.setDates, that);
    };
    
	/**
	 * 
	 */
    this.initData = function(){
    	data.recalculateLastIndex();
    	that.setDates();
    };
    
	/**
	 * 
	 * @param {Object} participantIndex
	 */
    this.markByParticipantId = function(participantIndex){
        var participantId = data.Participants[participantIndex - 1].id;
        var votes = data.getVotesByParticipant(participantId);
        
        for (var i = 0; i < votes.length; i++) {
            var tr = null;
            for (var j=0;j<tableMarkup.rows.length;j++ ) {
                if (tableMarkup.rows[j].optionId==votes[i].optionId) {
                    tr = tableMarkup.rows[j];
                    break;
                }
            }
            switch (Number(votes[i].pref)) {
                case 1:
                    YAHOO.util.Dom.addClass(tr, "rowYes");
                    break;
                case 2:
                    YAHOO.util.Dom.addClass(tr, "rowMaybe");
                    break;
                case 3:
                    YAHOO.util.Dom.addClass(tr, "rowNo");
                    break;
            }
        }
    };
    
	/**
	 * 
	 * @param {Object} dateStr
	 */
    this.markByDate = function(dateStr){
        var choice = data.getChoiceByDateStr(dateStr);
        var choiceIds = [];
        var i=0;
        if (choice != -1) {
        	choiceIds.push(choice.optionId);
        	if(choice.free!==undefined && choice.free.length>0) {
        		for(i=0;i<choice.free.length;i++) {
        			choiceIds.push(choice.free[i].optionId);
        		}
        	}
            var tr = null;
            for (var j=0;j<tableMarkup.rows.length;j++ ) {
                if (choiceIds.indexOf(tableMarkup.rows[j].optionId)>-1) {
                    tr = tableMarkup.rows[j];
            		YAHOO.util.Dom.addClass(tr, "highlightedRow");
                }
            }
        }
    };
    
	/**
	 * 
	 */
    this.removeMarks = function(){
		for (var j=0;j<tableMarkup.rows.length;j++ ) {
            YAHOO.util.Dom.removeClass(tableMarkup.rows[j], "rowYes");
            YAHOO.util.Dom.removeClass(tableMarkup.rows[j], "rowMaybe");
            YAHOO.util.Dom.removeClass(tableMarkup.rows[j], "rowNo");
            YAHOO.util.Dom.removeClass(tableMarkup.rows[j], "highlightedRow");
        }
    };
    
	/**
	 * 
	 * @param {Object} eventType
	 * @param {Object} args
	 */
    this.voteChanged = function(eventType, args){
        
    };
    
    /* Event handlers */
    if (config.dataModel) {
        this.setModel(config.dataModel);
    }
    
    /* Events */
	/**
	 * @event onMouseOver
	 */
    this.onMouseOver = new YAHOO.util.CustomEvent("onMouseOver", that);
	
	/**
	 * @event onMouseOut
	 */
    this.onMouseOut = new YAHOO.util.CustomEvent("onMouseOut", that);   
    
    this.addTextChoiceEvent = new YAHOO.util.CustomEvent("addTextChoiceEvent", that);
    this.deleteChoiceEvent = new YAHOO.util.CustomEvent("deleteChoiceEvent", that);
    this.addFreeChoiceEvent= new YAHOO.util.CustomEvent("addFreeChoiceEvent", that);
    Choices.superclass.constructor.apply(this, [containerId, config]);
};
YAHOO.extend(Choices, Panel);

var CalendarPanel = function(containerId, config) {
    var that = this;
    var data;
    var user = config.userModel;
    var popup;
    var calendarConfig = config;
    
    /* Creating markup */
	/**
	 * @property header
	 * @type HTMLDivElement
	 */
    this.header = document.createElement('div');
    this.header.className = 'header';
    var headerText = document.createElement('span');
    headerText.className = 'headerText';
    headerText.innerHTML = resource.label.calendar;
    this.header.appendChild(headerText);
    
	/**
	 * @property body
	 * @type HTMLTableElement
	 */    
    this.body = document.createElement('div');
    this.body.id = containerId + '-calendar-container';
    this.body.className = 'container';

    
    CalendarPanel.superclass.constructor.apply(this, [containerId, {
    	"items" : {
    		"Calendar": new Calendar(this.body,calendarConfig)
    	} 
    }]);

}
YAHOO.extend(CalendarPanel, Panel);


/**
 * Calendar
 * @class Calendar
 * @extends YAHOO.widget.Calendar
 * @constructor
 * @param {Object} containerId
 * @param {Object} config
 */
var Calendar = function(containerId, config){
    var that = this;
    var data;
    var user = config.userModel;
    var popup;
    config = config ||
    {};
    this.listeners = config.listeners ||
    {};
    
	/**
	 * 
	 * @param {Object} cellIndex
	 */
    this.selectCell = function(cellIndex){
        if (config.allowSelect !== false) {
            Calendar.superclass.selectCell.call(this, cellIndex);
        }
        return true;
    };
    
	/**
	 * 
	 * @param {Object} e
	 * @param {Object} cal
	 */
    this.doCellMouseOver = function(e, cal){
        var target;
        var date;
        if (e) {
            target = YAHOO.util.Event.getTarget(e);
        }
        else {
            target = this;
        }
        
        while (target.tagName && target.tagName.toLowerCase() != "td") {
            target = target.parentNode;
            if (!target.tagName || target.tagName.toLowerCase() == "html") {
                return;
            }
        }
        
        if (YAHOO.util.Dom.hasClass(target, "choice-detail-cell") ||
        		YAHOO.util.Dom.hasClass(target, cal.Style.CSS_CELL_SELECTED)) {
            YAHOO.util.Dom.addClass(target, cal.Style.CSS_CELL_HOVER);
        }
        
        date = cal.getDateByCellId(target.id);
        if (date) 
            that.onCellMouseOver.fire(date.toString("MM/dd/yyyy"));
    };
    
	/**
	 * 
	 * @param {Object} e
	 * @param {Object} cal
	 */
    this.doCellMouseOut = function(e, cal){
        var target;
        if (e) {
            target = YAHOO.util.Event.getTarget(e);
        }
        else {
            target = this;
        }
        
        while (target.tagName && target.tagName.toLowerCase() != "td") {
            target = target.parentNode;
            if (!target.tagName || target.tagName.toLowerCase() == "html") {
                return;
            }
        }
        
        if (YAHOO.util.Dom.hasClass(target, "choice-detail-cell") ||
        		YAHOO.util.Dom.hasClass(target, cal.Style.CSS_CELL_SELECTED)) {
            YAHOO.util.Dom.removeClass(target, cal.Style.CSS_CELL_HOVER);
        }
        
        that.onCellMouseOut.fire();
    }
    
	/**
	 * 
	 * @param {Object} dataModel
	 */
    this.setModel = function(dataModel){
        data = dataModel;
        data.onDataLoaded.subscribe(that.initData, that);
        data.onVoteModified.subscribe(that.voteChanged, that);
        data.onChoiceRemoved.subscribe(that.initData, that);
    };
    
	/**
	 * 
	 */
    this.initData = function(){
        if (!data.Choices.length) {
            that.clear();
        }
        that.cfg.setProperty('pageDate', Date.parse(data.Choices[0].date).toString('MM/yyyy'));
        if (data.Choices.length) {
            var val = data.Choices[0].date;
            for (var i = 1; i < data.Choices.length; i++) 
                val = val + "," + data.Choices[i].date;
            that.cfg.setProperty('selected', val);
        }
        
        that.render();
        if (data.Choices.length) {
        	var choice;
            for (var i = 1; i < data.Choices.length; i++) {
            	choice = data.Choices[i];
                that.colorize(choice.optionId,2);
            }
        }
        that.markByParticipantId(config.tempname ? "tempId" : YAHOO.util.Cookie.get("pId"));
       
    };
    
	/**
	 * 
	 * @param {Object} participantIndex
	 */
    this.markByParticipantId = function(participantIndex){
        var participantId = data.Participants[participantIndex - 1].id;
        var votes = data.getVotesByParticipant(participantId);
        for (var i = 0; i < votes.length; i++) {
            var date = Date.parse(data.getChoiceById(votes[i].optionId).date);
            var cell = that.cells[that.getCellIndex(date)];
            switch (Number(votes[i].pref)) {
                case 1:
                    YAHOO.util.Dom.addClass(cell, "cellYes");
                    break;
                case 2:
                    YAHOO.util.Dom.addClass(cell, "cellMaybe");
                    break;
                case 3:
                    YAHOO.util.Dom.addClass(cell, "cellNo");
                    break;
            }
        }
    };
    
	/**
	 * 
	 * @param {Object} choiceId
	 */
    this.calculatePosition = function(date){
        var cell = that.cells[that.getCellIndex(Date.parse(date))];
        var choice = data.getChoiceByDate(date);
        var yes=0;
        var no=0;
        var total=0;
        var i;
        var choiceId;
        if (choice != -1) {
        	choiceId=choice.optionId;
        	yes += data.statistics[Number(choiceId).toString()].yes;
        	no += data.statistics[Number(choiceId).toString()].no;
        	total += data.statistics[Number(choiceId).toString()].total;
        	if(choice.free!==undefined && choice.free.length>0) {
        		for(i=0;i<choice.free.length;i++) {
        			choiceId=choice.free[i].optionId;
		        	yes += data.statistics[Number(choiceId).toString()].yes;
		        	no += data.statistics[Number(choiceId).toString()].no;
		        	total += data.statistics[Number(choiceId).toString()].total;
        		}
        	}
        }        
        var yes = parseInt(yes * 100 / total * 0.39);
        var no = parseInt(no * 100 / total * 0.39);
        var position = 39 + yes - no;
        
        return "0px -" + position + "px !important";
    };
    
	/**
	 * 
	 * @param {Object} choiceId
	 */
    this.markByChoiceId = function(choiceId){
        var cell = that.cells[that.getCellIndex(Date.parse(data.getChoiceById(choiceId).date))];
        YAHOO.util.Dom.addClass(cell, "choicerowhover");
    };
    
	/**
	 * 
	 * @param {Object} cell
	 * @param {Object} choice
	 */
    this.markCellByChoice = function(cell, choice){
        var choiceId = data.getChoiceIdByDate(that.getDateByCellId(cell.id));
        var userId = config.tempname ? "tempId" : YAHOO.util.Cookie.get("pId");
        data.setVote(choiceId, stringToPref(choice), userId);
    };
    
    this.colorize = function(optionId,pref) {
        var date = data.getChoiceById(optionId).date;
        var cell = that.cells[that.getCellIndex(Date.parse(date))];
        
        cell.style.cssText = "background-position: " + that.calculatePosition(date) + ";";
        
        YAHOO.util.Dom.removeClass(cell, "userSelectedYes");
        YAHOO.util.Dom.removeClass(cell, "userSelectedMaybe");
        YAHOO.util.Dom.removeClass(cell, "userSelectedNo");
        YAHOO.util.Dom.addClass(cell, "userSelected" + prefToCssString(pref));    	
    }
    
	/**
	 * 
	 * @param {Object} eventType
	 * @param {Object} args
	 */
    this.voteChanged = function(eventType, args){
        var optionId = args[0];
        var voterId = args[1];
        var pref = args[2];
        that.colorize(optionId,pref);
    };
    
	/**
	 * 
	 */
    this.removeMarks = function(){
        var selDates = that.getSelectedDates();
        for (var i = 0; i < selDates.length; i++) {
            try {
                YAHOO.util.Dom.removeClass(that.cells[that.getCellIndex(selDates[i])], "cellYes");
                YAHOO.util.Dom.removeClass(that.cells[that.getCellIndex(selDates[i])], "cellMaybe");
                YAHOO.util.Dom.removeClass(that.cells[that.getCellIndex(selDates[i])], "cellNo");
                YAHOO.util.Dom.removeClass(that.cells[that.getCellIndex(selDates[i])], "choicerowhover");
            } 
            catch (e) { /* array indexing exception caught */
            }
        }
    };

	/**
	 * 
	 * @param {Object} cellIndex
	 */
    this.deselectCell = function(cellIndex) {
        if (config.allowSelect !== false) {
			var cell = this.cells[cellIndex],
			cellDate = this.cellDates[cellIndex],
			cellDateIndex = this._indexOfSelectedFieldArray(cellDate);

			var selectable = YAHOO.util.Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
			if (!selectable) { this.logger.log("The cell at cellIndex:" + cellIndex + " is not a selectable/deselectable cell", "info"); }
	
			if (selectable) {
				this.beforeDeselectEvent.fire();
	
				var selected = this.cfg.getProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.SELECTED.key),
					dCellDate = this._toDate(cellDate),
					selectDate = cellDate.concat();
	
				if (cellDateIndex > -1) {
					if (YAHOO.util.Dom.hasClass(cell, this.Style.CSS_CELL_SELECTED)) {
						YAHOO.util.Dom.removeClass(cell, this.Style.CSS_CELL_SELECTED);
						YAHOO.util.Dom.removeClass(cell, this.Style.CSS_CELL_HOVER);
					}
					selected.splice(cellDateIndex, 1);
				}
		
				if (this.parent) {
					this.parent.cfg.setProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.SELECTED.key, selected);
				} else {
					this.cfg.setProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.SELECTED.key, selected);
				}
		
				this.deselectEvent.fire(selectDate);
			}
        }
        if (config.allowPopup === true) {
            var cellId = -1;
            if (popup) {
                cellId = popup.cellId;
                popup.destroy();
                popup = null;
            }
            
            if (cellId != cellIndex) {
                var cell = that.cells[cellIndex];
                popup = new VoterPopup(document.body, cellIndex);
                
                popup.onClick.subscribe(function(type, selectionArray){                	
                    that.markCellByChoice(that.cells[selectionArray[0]], selectionArray[1]);
                    
                    popup.destroy();
                    popup = null;
                }, that);
                
                popup.element.style.position = "absolute";
                popup.element.style.top = YAHOO.util.Dom.getY(cell) + 16 + "px";
                popup.element.style.left = YAHOO.util.Dom.getX(cell) - 20 + "px";
            }
        }
		
		return this.getSelectedDates();
	}    
    
	/**
	 * 
	 * @param {Object} workingDate
	 * @param {Object} cell
	 */
    this.renderCellNotThisMonth = function(workingDate,cell) {
    	// do nothing, default render (selectable)
    };
    
	/**
	 * 
	 * @param {Object} workingDate
	 * @param {Object} cell
	 */
    this.renderCellDefault = function(workingDate,cell) {
    	cell.innerHTML = '<a href="#" class="' + this.Style.CSS_CELL_SELECTOR +
    			(workingDate.getMonth() != this.cfg.getProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.PAGEDATE.key).getMonth() ? ' oom-selectable' : '') + 
    			'">' + this.buildDayLabel(workingDate) + "</a>";
    }
      
    /* Event handlers */
    if (config.dataModel) {
        this.setModel(config.dataModel);
    }
    
    /* Events */
	/**
	 * @event onCellMouseOver
	 */
    this.onCellMouseOver = new YAHOO.util.CustomEvent("onCellMouseOver", that);
	/**
	 * @event onCellMouseOut
	 */
    this.onCellMouseOut = new YAHOO.util.CustomEvent("onCellMouseOut", that);
    
    Calendar.superclass.constructor.apply(this, [containerId, config]);
    
    for (var listener in this.listeners) {
        this[listener].subscribe(this.listeners[listener], this);
    }
    if (config.enableColorization !== false) {
        this.renderEvent.subscribe(function(){
            for (var i = 0; i < data.Choices.length; i++) {
                var date = Date.parse(data.Choices[i].date);
                try {
                    var cell = that.cells[that.getCellIndex(date)];
                    var savedPref = data.getVoteByVoterChoice(data.Choices[i].optionId, config.tempname ? "tempId" : YAHOO.util.Cookie.get("pId"));
                    
                    YAHOO.util.Dom.addClass(cell, "choice-detail-cell");
                    switch (savedPref) {
                        case 1:
                            YAHOO.util.Dom.addClass(cell, "userSelectedYes");
                            break;
                        case 2:
                            YAHOO.util.Dom.addClass(cell, "userSelectedMaybe");
                            break;
                        case 3:
                            YAHOO.util.Dom.addClass(cell, "userSelectedNo");
                            break;
                    }
                    
                    var yes = parseInt(data.statistics[data.Choices[i].optionId.toString()].yes * 100 / data.statistics[data.Choices[i].optionId.toString()].total * 0.4);
                    var no = parseInt(data.statistics[data.Choices[i].optionId.toString()].no * 100 / data.statistics[data.Choices[i].optionId.toString()].total * 0.4);
                    
                    var position = 39 + yes - no;
                    
                    cell.style.cssText = "background-position: 0px -" + position + "px !important;";
                } 
                catch (e) { /* array indexing exception caught */
                }
            }
        }, this);
    }
    
};
YAHOO.extend(Calendar, YAHOO.widget.Calendar);

var TextWidget = function(containerId, config) {
	var that = this;
	config = config || {};
	
    this.header = document.createElement('div');
    this.header.className = 'header';
    var headerText = document.createElement('span');
    headerText.className = 'headerText';
    headerText.innerHTML = "Add freetext choices";
    this.header.appendChild(headerText);
    
    this.body = document.createElement("div");
    this.body.className = "container";
    
    var inp = document.createElement("input");
    var but = document.createElement("div");
    but.innerHTML='<img src="/assets/img/add.png">';
    but.className="addButton";
    but.onclick=function() {
    	if(inp.value) {
    		that.selectEvent.fire(inp.value);
    	}
    }
    
    this.body.appendChild(inp);
    this.body.appendChild(but);
    

    this.selectEvent= new YAHOO.util.CustomEvent("selectEvent", that);
    
    TextWidget.superclass.constructor.apply(this, [containerId, config]);
    
}
YAHOO.extend(TextWidget, Panel);


/**
 * Paricipants
 * @class Participants
 * @constructor
 * @extends Panel
 * @param {Object} containerId
 * @param {Object} config
 */
var Participants = function(containerId, config){
    var that = this;
    config = config ||
    {};
	/**
	 * @property header
	 * @type HTMLDivElement
	 */
    this.header = document.createElement('div');
    this.header.className = 'header';
    var headerText = document.createElement('span');
    headerText.className = 'headerText';
    headerText.innerHTML = resource.label.participants;
    this.header.appendChild(headerText);
    
	/**
	 * @property body
	 * @type HTMLUlElement
	 */
    this.body = document.createElement('ul');
    this.body.className = "container";

	/**
	 * @property footer
	 * @type HTMLDivElement
	 */
    this.footer = document.createElement('div');
    this.footer.className = 'particpantsHint';
    this.footer.innerHTML = resource.instruction.participantHint;
    
    
    var prevStyle;
    var markupMapping = {};
    var data;
    var user = config.userModel;
    
	/**
	 * @method pOver
	 * @private
	 * @param {Object} e
	 */
    var pOver = function(e){
        var eObj = e || window.event;
        
        // Nem IE
        var target = eObj.currentTarget || eObj.srcElement || eObj.toElement;
        
        // megkeresni a sort, amin mozgok - IE miatt
        while (target.tagName != 'LI') {
            target = target.parentNode;
        }
        
        if (target.className != 'participant highlightedParticipant') {
            prevStyle = target.className;
        }
        
        target.className = 'participant highlightedParticipant';
        that.onMouseOver.fire(indexOf(that.body.childNodes, target) + 1);
    };
    
    
	/**
	 * 
	 * @param {Object} pData
	 */
    this.addParticipant = function(pData){
    	
    	
    	
        var pLi = document.createElement('li');
				var pHolder = document.createElement('div');
        var pName = document.createElement('div');
        var pOpinion = document.createElement('div');
        markupMapping[pData.id] = pLi;
        pLi.onmouseover = pOver;
        pLi.onmouseout = function(){
            pLi.className = prevStyle;
            that.onMouseOut.fire();
        };
        var userId = config.tempname ? "tempId" : YAHOO.util.Cookie.get('pId');
        
        pLi.className = 'participant participantMaybe';
        if(config.owner && pData.id != userId) {
        	var delHtml = document.createElement("div");
        	delHtml.className = "participantDelete";
        	delHtml.innerHTML = '<img src="/assets/img/admin_delete.gif" />';
        	delHtml.onclick = function() {
						var d = window.confirm("Do you really want to delete this participant?");
        		if(d===true) {
							pollModel.removeParticipantById(pData.id);
						}
        	}
        	pLi.appendChild(delHtml);
					pHolder.className = "participantHolder";
        }
        pName.className = 'participantName';
        pName.innerHTML =
        		config.renderLoggedUser && pData.id == userId ?
        				config.renderLoggedUser(escapeHTML(pData.name)) :
        				'<span style="float:left">' + escapeHTML(pData.name) + '</span>';
				pHolder.appendChild(pName);
        pOpinion.className = 'participantOpinion';
        if (pData.opinion) {
            pOpinion.innerHTML = resource.punctuation.beginQuote + escapeHTML(pData.opinion) + resource.punctuation.endQuote;
        }
        pHolder.appendChild(pOpinion);
        
        if (config.hideLoggedUser === true &&
        pData.id == userId) {
            pLi.style.display = "none";
        }

        pLi.appendChild(pHolder);        
        that.body.appendChild(pLi);
    };
    
	/**
	 * 
	 * @param {Object} dataModel
	 */
    this.setModel = function(dataModel){
        data = dataModel;
        data.onDataLoaded.subscribe(that.initData, that);
        data.onOpinionModified.subscribe(that.opinionModified, that);
    };
    
    this.clear = function() {
    	that.body.innerHTML="";
    }
    
	/**
	 * 
	 */
    this.initData = function(){
    	that.clear();
        for (var i = 0; i < data.Participants.length; i++) {
            that.addParticipant(data.Participants[i]);
        }
    };
    
	/**
	 * 
	 * @param {Object} type
	 * @param {Object} args
	 */
    this.opinionModified = function(type, args){
        var voterId = args[0];
        var opinion = args[1];
        var divs = markupMapping[voterId].getElementsByTagName('div');
        var i,d;
        for(i=0;i<divs.length;i++) {
        	d=divs[i];
        	if(d.className!="participantOpinion") continue;
	        if (opinion) {
	            d.innerHTML = resource.punctuation.beginQuote + escapeHTML(opinion) + resource.punctuation.endQuote;
	        }
	        else {
	            d.innerHTML = "";
	        }
        	
        }
    };
    
	/**
	 * 
	 * @param {Object} choiceId
	 */
    this.markByChoiceId = function(choiceId){
        var votes = data.getVotesByChoiceId(choiceId);
        for (var i = 0; i < votes.length; i++) {
            pref = Number(votes[i].pref);
            voterLi = markupMapping[votes[i].voterId];
            switch (pref) {
                case 1:
                    voterLi.className = "participant participantYes";
                    break;
                case 2:
                    voterLi.className = "participant participantMaybe";
                    break;
                case 3:
                    voterLi.className = "participant participantNo";
                    break;
            }
        }
    };
    
	/**
	 * 
	 * @param {Object} dateStr
	 */
    this.markByDate = function(dateStr){
        var choiceId = data.getChoiceIdByDateStr(dateStr);
        if (choiceId != -1) {
            that.markByChoiceId(choiceId);
        }
    };
    
	/**
	 * 
	 */
    this.removeMarks = function(){
        for (i in markupMapping) {
            markupMapping[i].className = "participant participantMaybe";
        }
    };
    
    /* Event handlers */
    if (config.dataModel) {
        this.setModel(config.dataModel);
    }
    
    /* Events */
	/**
	 * @event onMouseOver
	 */
    this.onMouseOver = new YAHOO.util.CustomEvent("onMouseOver", that);
	
	/**
	 * @event onMouseOut 
	 */
    this.onMouseOut = new YAHOO.util.CustomEvent("onMouseOut", that);

    this.onParticipantDelete = new YAHOO.util.CustomEvent("onParticipantDelete", that);
    
    Participants.superclass.constructor.apply(this, [containerId, config]);
};
YAHOO.extend(Participants, Panel);

/**
 * Opinion
 * @class Opinion
 * @extends Panel
 * @constructor
 * @param {Object} containerId
 * @param {Object} config
 */
var Opinion = function(containerId, config){
    var that = this;
    config = config ||
    {};
    var data = {};
    this.dirty = false;
    
	/**
	 * @property header
	 * @type HTMLDivElement
	 */
    this.header = document.createElement('div');
    this.header.className = 'header';
    var headerText = document.createElement('span');
    headerText.className = 'headerText';

    if (config.closing) {
        headerText.innerHTML = resource.instruction.finalRemark;
    }
    else {
    	headerText.innerHTML = resource.instruction.participantOpinion;
    }
    this.header.appendChild(headerText);
    this.spinner = new Spinner(this.header);

    /**
	 * @property body
	 * @type HTMLDivElement
	 */
    this.body = document.createElement('div');
    this.body.id = containerId + '-container';
    this.body.className = "container";
    
	/**
	 * @property Opinion
	 * @type HTMLInputElement (type=text)
	 */
    this.Opinion = document.createElement("input");
    this.Opinion.type = "text";
    this.body.appendChild(this.Opinion);
    
	/**
	 * 
	 * @param {Object} e
	 */
    this.opinionChanged = function(e){
        if (config.closing !== true) {
            data.setOpinion(config.tempname ? "tempId" : YAHOO.util.Cookie.get('pId'), that.Opinion.value);
        }
    };
    
	/**
	 * 
	 */
    this.initData = function(){
        if (config.closing !== true) {
            that.Opinion.value = data.getVoterById(config.tempname ? "tempId" : YAHOO.util.Cookie.get('pId')).opinion;
            that.Opinion.onkeyup = that.opinionChanged;
        }
    };
    
	/**
	 * 
	 * @param {Object} dataObj
	 */
    this.setModel = function(dataObj){
        data = dataObj;
        data.onDataLoaded.subscribe(that.initData, that);
    };
    
    if (config.dataModel) {
        this.setModel(config.dataModel);
    }

    
    Opinion.superclass.constructor.apply(this, [containerId, config]);
};
YAHOO.extend(Opinion, Panel);

/**
 * Poll Widget
 * @class Poll
 * @constructor
 * @extends Panel
 * @param {Object} containerId
 * @param {Object} config
 */
var Poll = function(containerId, config){
    var that = this;
    config = config ||
    {};
    /**
     * @property {Object} pollModel
     * @private
     */
    var pollModel = config.pollModel ||
    {};
    
    var choices = new Choices(containerId + "-choices", {
        renderEmptyInfo: function(){
	    },
	    dataModel: config.pollModel,
	    listeners: {
	        onMouseOver: function(type, args){
	            that.items["Participants"].markByChoiceId(args[0]);
	            that.items["CalendarPanel"].items['Calendar'].markByChoiceId(args[0]);
	        },
	        onMouseOut: function(type){
	            that.items["Participants"].removeMarks("Maybe");
	            that.items["CalendarPanel"].items['Calendar'].removeMarks();
	        }
	    },
	    tempname: config.tempname,
	    closing: config.closing === true
    });
    
    var opinion = new Opinion(containerId + "-opinion", {
        dataModel: config.pollModel,
        tempname: config.tempname,
        closing: config.closing === true
    });
    
    var opinionTimer;    
    
    function saveChanges() {
    	var changes = YAHOO.lang.JSON.stringify({
			Votes: pollModel.getVotesByParticipant(config.tempname ? "tempId" : YAHOO.util.Cookie.get("pId") ),
			Opinion: opinion.Opinion.value,
			Name: config.tempname
		});
		
		pollModel.dirty = false;
		
		var obj = {
            params: "changes=" + changes,
            url: '/vote/' + pollModel.id + '/',
            spinners:[]
		};

		if(opinion.dirty) {
			obj.spinners.push(opinion.spinner);
			opinion.dirty = false;
		}
		if(choices.dirty) {
			obj.spinners.push(choices.spinner);
			choices.dirty = false;
		}
		Queue.add(obj);
    }
    
    if (pollModel.onVoteModified) {
    	pollModel.onVoteModified.subscribe(function(eventType, args) {
    		if (opinionTimer) {
    			clearTimeout(opinionTimer);
    		}
    		choices.dirty = true;
    		opinionTimer = setTimeout(saveChanges, 1000);
    	}, that);
    }
    
    if (pollModel.onOpinionModified) {
    	pollModel.onOpinionModified.subscribe(function(eventType, args) {
    		if (opinionTimer) {
    			clearTimeout(opinionTimer);
    		}    		
    		opinion.dirty = true;
    		opinionTimer = setTimeout(saveChanges, 1000);
    	}, that);
    }
    
    this.items = {
        "LeftSide": new Panel(containerId + "-leftside", {
            items: {
                "Choices": choices,
                "Opinion": opinion
            }
        }),
        "CalendarPanel": new CalendarPanel(containerId + "-calendar", {
            multi_select: true,
            allowSelect: false,
            allowPopup: false,
            dataModel: config.pollModel,
            tempname: config.tempname,
            listeners: {
                onCellMouseOver: function(type, dateStr){
                    that.items["Participants"].markByDate(dateStr);
                    that.items["LeftSide"].items["Choices"].markByDate(dateStr);
                },
                onCellMouseOut: function(type){
                    that.items["Participants"].removeMarks();
                    that.items["LeftSide"].items["Choices"].removeMarks();
                }
            },
            MY_LABEL_MONTH_POSITION: resource.format.YAHOO.widget.Calendar.MY_LABEL_MONTH_POSITION,
            MY_LABEL_MONTH_SUFFIX: resource.format.YAHOO.widget.Calendar.MY_LABEL_MONTH_SUFFIX,
            MY_LABEL_YEAR_POSITION: resource.format.YAHOO.widget.Calendar.MY_LABEL_YEAR_POSITION,
            MY_LABEL_YEAR_SUFFIX: resource.format.YAHOO.widget.Calendar.MY_LABEL_YEAR_SUFFIX,
            WEEKDAYS_SHORT: resource.format.YAHOO.widget.Calendar.WEEKDAYS_SHORT,
            MONTHS_LONG: resource.format.YAHOO.widget.Calendar.MONTHS_LONG,
            START_WEEKDAY: resource.format.YAHOO.widget.Calendar.START_WEEKDAY
        }),
        "Participants": new Participants(containerId + "-participants", {
            dataModel: config.pollModel,
            tempname: config.tempname,
            owner: config.owner,
            renderLoggedUser: function(name){
                return '<span style="font-style: italic;">' + name + ' (Me)' + '</span>';
            },
            hideLoggedUser: false,
            listeners: {
                onMouseOver: function(type, participantId){
                    that.items["LeftSide"].items["Choices"].markByParticipantId(participantId);
                    that.items["CalendarPanel"].items['Calendar'].markByParticipantId(participantId)
                },
                onMouseOut: function(type){
                    that.items["LeftSide"].items["Choices"].removeMarks();
                    that.items["CalendarPanel"].items['Calendar'].removeMarks();
                }
            }
        })
    };
    Poll.superclass.constructor.apply(this, [containerId, config]);
};
YAHOO.extend(Poll, Panel);

/**
 * Create Poll widget
 * @class CreatePoll
 * @constructor
 * @extends Panel
 * @param {Object} containerId
 * @param {Object} config
 */
var CreatePoll = function(containerId, config){
    var that = this;
    config = config ||
    {};
    var pollModel = config.pollModel ||
    {};
    this.items = {
        "CalendarPanel": new CalendarPanel(containerId + "-calendar", {
            multi_select: true,
            enableColorization: false,
            dataModel: pollModel,
            mindate: config.mindate,
            listeners: {
                onCellMouseOver: function(type, dateStr){
                    that.items["Choices"].markByDate(dateStr);
                },
                onCellMouseOut: function(type){
                    that.items["Choices"].removeMarks();
                },
                selectEvent: function(type, args, obj){
                    var selected = args[0];
                    var selDate = obj.toDate(selected[0]).toString("MM/dd/yyyy");
                    pollModel.addChoice(selDate);
                },
                deselectEvent: function(type, args, obj){
                    var selected = args; // hack because of overridden deselectCell
                    var selDate = obj.toDate(selected[0]).toString("MM/dd/yyyy");
                    pollModel.removeChoice(selDate);
                }
            },
            MY_LABEL_MONTH_POSITION: resource.format.YAHOO.widget.Calendar.MY_LABEL_MONTH_POSITION,
            MY_LABEL_MONTH_SUFFIX: resource.format.YAHOO.widget.Calendar.MY_LABEL_MONTH_SUFFIX,
            MY_LABEL_YEAR_POSITION: resource.format.YAHOO.widget.Calendar.MY_LABEL_YEAR_POSITION,
            MY_LABEL_YEAR_SUFFIX: resource.format.YAHOO.widget.Calendar.MY_LABEL_YEAR_SUFFIX,
            WEEKDAYS_SHORT: resource.format.YAHOO.widget.Calendar.WEEKDAYS_SHORT,
            MONTHS_LONG: resource.format.YAHOO.widget.Calendar.MONTHS_LONG,
            START_WEEKDAY: resource.format.YAHOO.widget.Calendar.START_WEEKDAY
        }),
        "Choices": new Choices(containerId + "-choices", {
            renderHeader: false,
            renderStatistics: false,
            allowDelete: true,
            dataModel: pollModel,
            listeners: {
                onMouseOver: function(type, choiceId){
                    that.items["CalendarPanel"].items['Calendar'].markByChoiceId(choiceId);
                },
                onMouseOut: function(type){
                    that.items["CalendarPanel"].items['Calendar'].removeMarks();
                },
                addTextChoiceEvent: function(type,args) {
                	pollModel.addTextChoiceById(args[0],"");
                },
                deleteChoiceEvent: function(type,args) {
                	pollModel.removeChoiceById(args[0]);                	
                },
                addFreeChoiceEvent: function() {
                	var index = pollModel.addChoice(null,resource.label.otheroptions);
                	pollModel.addTextChoiceById(index,"");
                }
            }
        })
    };
    CreatePoll.superclass.constructor.apply(this, [containerId, config]);
}
YAHOO.extend(CreatePoll, Panel);

/**
 * Preview Poll widget
 * @class PreviewPoll
 * @constructor
 * @extends Panel
 * @param {Object} containerId
 * @param {Object} config
 */
var PreviewPoll = function(containerId, config){
    var that = this;
    config = config ||
    {};
    var pollModel = config.pollModel ||
    {};
    
    this.items = {
        "CalendarPanel": new CalendarPanel(containerId + "-calendar", {
            multi_select: true,
            enableColorization: false,
            dataModel: pollModel,
            allowSelect: false,
            listeners: {
                onCellMouseOver: function(type, dateStr){
                    that.items["Choices"].markByDate(dateStr);
                },
                onCellMouseOut: function(type){
                    that.items["Choices"].removeMarks();
                }
            },
            MY_LABEL_MONTH_POSITION: resource.format.YAHOO.widget.Calendar.MY_LABEL_MONTH_POSITION,
            MY_LABEL_MONTH_SUFFIX: resource.format.YAHOO.widget.Calendar.MY_LABEL_MONTH_SUFFIX,
            MY_LABEL_YEAR_POSITION: resource.format.YAHOO.widget.Calendar.MY_LABEL_YEAR_POSITION,
            MY_LABEL_YEAR_SUFFIX: resource.format.YAHOO.widget.Calendar.MY_LABEL_YEAR_SUFFIX,
            WEEKDAYS_SHORT: resource.format.YAHOO.widget.Calendar.WEEKDAYS_SHORT,
            MONTHS_LONG: resource.format.YAHOO.widget.Calendar.MONTHS_LONG,
            START_WEEKDAY: resource.format.YAHOO.widget.Calendar.START_WEEKDAY
        }),
        "Choices": new Choices(containerId + "-choices", {
            renderStatistics: false,
            dataModel: pollModel,
            renderHeader: false,
            renderEmptyInfo: function(){
            },
            listeners: {
                onMouseOver: function(type, choiceId){
                    that.items["CalendarPanel"].items['Calendar'].markByChoiceId(choiceId);
                },
                onMouseOut: function(type){
                    that.items["CalendarPanel"].items['Calendar'].removeMarks();
                }
            }
        })
    }
    PreviewPoll.superclass.constructor.apply(this, [containerId, config]);
};
YAHOO.extend(PreviewPoll, Panel);

/**
 * Votes Summary widget
 * @class VotesSummary
 * @constructor
 * @param {Object} containerId
 * @param {Object} config
 */
var VotesSummary = function(containerId, config){
    var that = this;
    config = config ||
    {};
    var data;
    var yesParticipants = document.getElementById('yesParticipants');
    var maybeParticipants = document.getElementById('maybeParticipants');
    var noParticipants = document.getElementById('noParticipants');
    
	/**
	 * 
	 * @param {Object} pData
	 */
    this.addVote = function(pData){
        var body;
        switch (Number(pData.pref)) {
            case 1:
                body = yesParticipants;
                break;
            case 2:
                body = maybeParticipants;
                break;
            case 3:
                body = noParticipants;
                break;
        }
        var part = data.getVoterById(pData.voterId);
        
        var div = document.createElement("div");
        
        var partName = document.createElement("div");
        partName.className = 'participantName';
        partName.innerHTML = escapeHTML(part.name);
        div.appendChild(partName);
        
        var partOpinion = document.createElement("div");
        if (part.opinion) {
            partOpinion.className = 'participantOpinion';
            partOpinion.innerHTML = resource.punctuation.beginQuote + escapeHTML(part.opinion) + resource.punctuation.endQuote;
            div.appendChild(partOpinion);
        }
        
        body.appendChild(div);
    }
    
	/**
	 * 
	 */
    this.initData = function(){
        if (data.Result || data.ResultId) {
            document.getElementById(containerId).style.display = 'block';
            if(data.ResultId) {
            	var votes = data.getVotesByChoiceId(data.ResultId);
            }
            else if(data.Result) {
	            var votes = data.getVotesByDateStr(data.Result);
            }
            for (var i = 0; i < votes.length; i++) {
                that.addVote(votes[i]);
            }            	
        }
        else {
            document.getElementById(containerId).style.display = 'none';
        }
    };
    
	/**
	 * 
	 * @param {Object} dataModel
	 */
    this.setModel = function(dataModel){
        data = dataModel;
        data.onDataLoaded.subscribe(that.initData, that);
    };
    
    if (config.dataModel) {
        this.setModel(config.dataModel);
    }
};
