
/**
 * Submits the main form asynchronously, and follows the given link afterwards.
 *
 * @param	anchor	Link to follow after completion
 */
function poll_asyncSubmit(anchor) {
			
	var href = document.location.href;
	
	if (href.indexOf('?') != -1)
		href += '&asyncSubmit';
	else
		href += '?asyncSubmit';
	
//	alert('poll_asyncSubmit(): Saving form asynchronously...');
	
	new Ajax.Request(href, {parameters:Form.serialize(poll_getMainForm()), onComplete:function(){
//		alert('poll_asyncSubmit(): Form saved, following link');
		document.location.href = anchor.href;
	}});
	
	return false;
	
}

/**
 * Returns a reference to the main form (ie. the first form encountered from the document.forms array).
 * 
 */
function poll_getMainForm() {
	
	var form = document.forms[0];
	
	if (!form)
		return alert("poll_getMainForm(): Main form not available");
	
	return form;
	
}

/**
 * Opens a pop-up window.
 *
 * @param	URL			Surprisingly, the URL to open in the pop-up window
 * @param	winW		Width of the window
 * @param	winH		Height of the window
 * @param	windowID	Handle for the window (optional)
 */
function poll_popUp(URL, winW, winH, windowID) {
	
	var dims = poll_getViewportDimensions();
	var winX = dims['x'] / 2 - winW / 2;
	var winY = dims['y'] / 2 - winH / 2;
	
	if (typeof(windowID) != 'string')
		windowID = 'poll_popup';
	
	newWindow = window.open(URL, windowID, 'toolbar=0,scrollbars=1,location=0,statusbar=0,menubar=0,resizable=0,width=' + winW + ',height=' + winH + ',left=' + winX + ',top=' + winY);
	
	if (window.focus)
		newWindow.focus();
	
	return false;
	
}

/**
 * Returns the dimensions of the viewport, compensating for different browsers.
 *
 */
function poll_getViewportDimensions() {
	
	var dims = new Array(); // Viewport dimensions
	
	if (self.innerHeight) { // All except Explorer
		
		dims["x"] = self.innerWidth;
		dims["y"] = self.innerHeight;
		
	} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
		
		dims["x"] = document.documentElement.clientWidth;
		dims["y"] = document.documentElement.clientHeight;
		
	} else if (document.body) { // Other Explorers
		
		dims["x"] = document.body.clientWidth;
		dims["y"] = document.body.clientHeight;
		
	}
	
	return dims;
	
}

/**
 * Returns a reference to the element with the given ID (possibly given without the 'poll_' prefix).
 *
 * If the parameter already is a reference to an object, returns that reference.
 *
 * @param element	Element to refer to
 */
function poll_getElement(element) {
	
	if (typeof(element) == 'string')
		if (element.substr(0, 5) == 'poll_')
			element = document.getElementById(element);
		else
			element = document.getElementById('poll_' + element);
		
	return element;
	
}

/**
 * Sets the visibility of the indicated element.
 * 
 * If no effect-value is given, the default is assumed.  Note that this may change, so don't depend on this.  If the
 * element's visibility is already the one given as visibility, no changes are made.
 *
 * @param element	Element to show/hide
 * @param visible	New visibility of element
 * @param effect	Show/hide the element with or without an effect
 */
function poll_setVisible(element, visible, effect) {

	effect	= effect == null ? true : effect;
	element	= poll_getElement(element);

	if (!element)
		return alert("poll_setVisible(): Invalid 'element' parameter");
		
	if (
		visible
		&&
		element.style.display == 'none'
		)
		if (effect)
//			new Effect.BlindDown(element, {queue: 'end', duration: 0.4});
			new Effect.BlindDown(element, {duration: 0.4});
		else
			element.style.display = 'block';
	else if (
		!visible
		&&
		(
			element.style.display == 'block'
			||
			element.style.display == ''
		)
		)
		if (effect)
//			new Effect.BlindUp(element, {queue: 'end', duration: 0.4});
			new Effect.BlindUp(element, {duration: 0.4});
		else
			element.style.display = 'none';
	
}

/**
 * Sets the visibility of the indicated element by reading the checked-value of the given checkbox.
 * 
 * Note that the checkbox may also be a radio box.
 *
 * @param checkbox	Element to determine visibility
 * @param element	Element to show/hide
 * @param effect	Show/hide the element with or without an effect
 */
function poll_determineVisible(checkbox, element, effect) {
	
	checkbox	= poll_getElement(checkbox);
	element		= poll_getElement(element);
		
	if (!checkbox)
		return alert("poll_determineVisible(): Invalid 'checkbox' parameter");
		
	if (!element)
		return alert("poll_determineVisible(): Invalid 'element' parameter");
		
	poll_setVisible(element, checkbox.checked, effect);
	
}

var poll_sequenceSetVisible_topIndex = new Array();

/**
 * Sets the visibility of a sequence of elements, having a common idPrefix.
 * 
 * This function keeps track of it's calls, and calls poll_setVisible() only when called with an index higher than any
 * previous index for that particular idPrefix.
 * 
 * The most useful application for this function is displaying more elements in a series "when needed"; for example
 * when the user clicks on the last visible element of a series, the next becomes visible.
 *
 * @param idPrefix	Common prefix to the id's of all elements in this series
 * @param index		Index (zero-based) of the element to show (if not shown already)
 */
function poll_sequenceSetVisible(idPrefix, index) {
	
//	alert('poll_sequenceSetVisible(' + idPrefix + ', ' + index + ') called, poll_sequenceSetVisible_topIndex[idPrefix] = ' + poll_sequenceSetVisible_topIndex[idPrefix]);
	
	if (
		typeof(poll_sequenceSetVisible_topIndex[idPrefix]) != 'number'
		||
		poll_sequenceSetVisible_topIndex[idPrefix] < index
		) {

		poll_sequenceSetVisible_topIndex[idPrefix] = index;
		
		if (poll_getElement(idPrefix + index))
			poll_setVisible(idPrefix + index, true, true);
			
	}
	
}

/**
 * Sets the visibility of a sequence of elements, having a common idPrefix.
 * 
 * Initialization function for poll_sequenceSetVisible().
 *
 * @param idPrefix			Common prefix to the id's of all elements in this series
 * @param topVisibleIndex	Highest index to be visible after the call
 * @param topIndex			Highest index in series
 */
function poll_sequenceDetermineVisible(idPrefix, topVisibleIndex, topIndex) {

//	alert('poll_sequenceDetermineVisible(' + idPrefix + ', ' + topVisibleIndex + ', ' + topIndex + ') called');
	
	poll_sequenceSetVisible_topIndex[idPrefix] = topVisibleIndex;
	
	for (i = 0; i <= topIndex; i++)
		poll_setVisible(idPrefix + i, i <= topVisibleIndex, false);
	
}

/**
 * Formats the contents of the given field to given numeric specifications.
 *
 * @param field		Field that should be formatted
 * @param precision	Desired amount of digits after the decimal place (default: floating point)
 * @param minValue	Lowest allowable value (default: no low bound)
 * @param maxValue	Highest allowable value (default: no high bound)
 */
function poll_formatNumberField(field, precision, minValue, maxValue) {
	
	if (!field || field.value == null)
		return alert("poll_formatNumberField(): Invalid 'field' parameter");
	
	var newValue = parseFloat(field.value.replace(/,/, '.'));
	
	if (isNaN(newValue))
		newValue = 0;
	
	if (maxValue != null && newValue > maxValue)
		newValue = maxValue;
		
	if (minValue != null && newValue < minValue)
		newValue = minValue;
	
	if (precision != null)
		field.value = newValue.toFixed(precision);
	else
		field.value = newValue;
	
}

/**
 * Performs and reacts to an asynchronous answers check.
 * 
 * This function gets called when the participant wants to proceed to the next page.  The form gets submitted by an
 * Ajax-request, and the server validates the answers per each ActiveQuestion on the page.
 * 
 * Parsing of the actual server response is delegated to poll_asyncAnswersCheck_parse().
 * 
 * @param response	In original call null, gets a value when called as the Ajax.Request callback
 */
function poll_asyncAnswersCheck(response) {
	
	poll_asyncAnswersCheck_setWorking(true);			// Set the questions form status to "working"

	if (typeof(response) != 'object') {					// This is a function call resulting from the user clicking on the nextPage button
		
		new Ajax.Request('?asyncAnswersCheck', {parameters:Form.serialize(poll_getMainForm()), onComplete:poll_asyncAnswersCheck});
		
	} else if (typeof(response) == 'object') {			// This is a function call resulting from the server responding
		
		if (poll_asyncAnswersCheck_parse(response)) {	// No erroneous values were found on form
			
			var nextButton = poll_getElement('nextPageButtonHidden');
			
			if (!nextButton)
				return alert('poll_asyncAnswersCheck(): No hidden next button found on form');
			
			nextButton.click();							// Simulate a click on the hidden nextPage button
			
		} else {										// There were some erroneous values on the form
			
			poll_asyncAnswersCheck_setWorking(false);	// Release the "working" status
			
		}
		
	}
	
}

/**
 * Sets the state of the questions form as either "working" or not.
 * 
 * When the form is "working", a busy-indicator is displayed, and form buttons are disabled.
 * 
 * @param working	Boolean value indicating if the form is working
 */
function poll_asyncAnswersCheck_setWorking(working) {
	
	var indicator	= poll_getElement('asyncAnswersCheck_working');
	var nextButton	= poll_getElement('nextPageButton');
	var prevButton	= poll_getElement('prevPageButton');
	
	indicator.style.display = working ? 'block' : 'none';	// Display the working-indicator
	
	if (nextButton)											// If the questions form defines a visible nextButton
		nextButton.disabled = working;						// ...set its disabled-value
	
	if (prevButton)											// If the questions form defines a visible prevButton
		prevButton.disabled = working;						// ...set its disabled-value
	
}

/**
 * Updates the state of the error messages on the questions form, and returns a boolean value indicating if all answers
 * were valid according to the server.
 * 
 * This function gets called after poll_asyncAnswersCheck() has received an answer from the server side.  The single
 * parameter to this function is the response from the server.  The server responds with two comma-separated lists of
 * JS-namespaces.  These lists are in turn glued with a semicolon.  Each JS-namespace in the first list is the
 * namespace of a question whose answer is OK.  The second list is of those with erroneous values.
 * 
 * @param response	Return value from the server
 * @return			Were answers to all Questions OK
 */
function poll_asyncAnswersCheck_parse(response) {
	
	if (typeof(response) == 'object')							// If the parameter is still in its original object form
		response = response.responseText;						// ...convert to string before parsing
	
	responseTypes = response.split(';');						// Split the two lists
	
	for (i = 0; i < responseTypes.length; i++) {				// Iterate through both lists
		
		namespaces = responseTypes[i].split(',');				// Split each list

		for (j = 0; j < namespaces.length; j++) {				// Iterate through all namespaces in each list
			
			errMsg = poll_getElement(namespaces[j] + 'errMsg');
			
			if (!errMsg)										// If the Question hasn't defined an errMsg element on the form, skip it
				continue;
			
			if (errMsg.style.display != 'none' && i == 1)		// If the errMsg is already visible, and the answer to that question still was invalid
				new Effect.Shake(errMsg);						// ...highlight the error message
				
			poll_setVisible(namespaces[j] + 'errMsg', i == 1);	// Depending on the outer loop (which list we are iterating) show/hide the errMsg in question
			
		}
	
	}
	
	return responseTypes[1].length == 0;
	
}
